CHANGELOG.md. This page mirrors that file for the documentation site, with links pointed at these docs.
[0.6.1] — 2026-04-14
Added
ConversationEvent(autoplay_sdk.chatbot) — dataclass for chatbot session-link webhooks.BaseChatbotWritersession-link webhook flow —SESSION_LINK_WEBHOOK_TOPICS,extract_conversation_event(),_parse_session_link_webhook_payload()(subclasses override parse only).autoplay_sdk.integrations.intercom— webhook topic constants,intercom_chatbot_webhook_url(), optionalformat_reactive_session_link_script(no logging from this subpackage).
Removed
format_proactive_session_start_scriptand the proactive/sessions/startsnippet — use Intercom webhooks toPOST /chatbot-webhook/{product_id}; optionalformat_reactive_session_link_scriptremains forPOST /sessions/link.
Documentation
- Logging — logger hierarchy, app-owned
loggingconfiguration, third-party subclass guidance, changelog cross-link. - README — logging section aligned with the above; metrics pointer (
SdkMetricsHook). - Intercom integration — SDK helpers and connector mapping (webhooks, optional snippet, inbox UX).
Breaking changes
format_proactive_session_start_scriptremoved fromautoplay_sdk.integrations.intercom.
Deprecations
None.[0.6.0] — 2026-04-13
Documentation
-
BaseChatbotWriter — Note body format — BaseChatbotWriter now documents the full plain-text contract for
_post_notebodies (header viaformat_chatbot_note_header, sorted 1-based action lines, binning vs post-link, empty list, summary notes)._format_notedocstring points to that page as the single source of truth. -
Logging — New Logging reference page (module loggers,
%formatting,exc_info, structuredextra, secrets guidance including HTTP bodies, common logging mistakes). Quickstart links to it for discoverability.
Bug fixes / observability
-
BaseChatbotWriter— pre-link flush failurewarningnow includes structuredextra(session_id,product_id,conversation_id). -
BaseChatbotWriter— post-link debounced flush: if_post_notereturns no part id after the debounce buffer was popped, logs awarningwith the sameextrashape and explains that this flush is not retried automatically.
Breaking changes
None.Deprecations
None.[0.5.0] — 2026-04-10
New features
-
ActionsPayload.merge(payloads)— class method that merges a non-empty list ofActionsPayloadobjects for the same session into one. Actions are concatenated and re-indexed from0;user_id/emailresolved from the first non-Nonevalue;forwarded_atset to the latest timestamp. RaisesValueErroron empty input. -
AsyncAgentContextWriter(debounce_ms=N)— new optional constructor parameter for a per-session trailing-edge accumulation window. When> 0, multipleadd()calls arriving within the window are merged viaActionsPayload.merge()beforewrite_actionsis called, reducing destination API calls during event bursts. Default is0(no debounce — existing behaviour unchanged). -
BaseChatbotWriter(autoplay_sdk.chatbot) — new public base class providing the complete pre-link/post-link delivery policy for building chatbot destinations. Subclass it and implement_post_noteand_redact_part; pre-link buffering (sliding window), at-link flush (binned note), and post-link debouncing are all included.IntercomChatbotin the event connector already extends this class.
Breaking changes
None. All changes are additive:AsyncAgentContextWriter.__init__gainsdebounce_ms: int = 0— existing code passing positional or keyword arguments is unaffected.ActionsPayload.merge()is a new class method; no existing method is renamed or removed.BaseChatbotWriteris a new public export; no existing symbols are removed.
Deprecations
None.Bug fixes / error handling improvements
-
BaseChatbotWriter.on_session_linked— now no-ops when the sameconversation_idis passed again (idempotent guard). The product worker includes the conv_id on every batch for already-linked sessions; without this guard, each batch would cancel the in-flight 150ms post-link debounce task and restart the window, causing notes to be delayed indefinitely during fast user interactions. -
BaseChatbotWriter.on_session_linked— pre-link buffer (_pending) is now only cleared after_post_noteconfirms success (returns a non-Nonepart id). Previously the buffer was popped before the API call; a transient Intercom failure would permanently lose those events. On failure the buffer is now preserved and the_conv_mapentry is rolled back so the nexton_session_linkedcall retries automatically. -
BaseChatbotWriter.write_actions: the post-link debounceasyncio.Tasknow has adone_callbackthat logs any unhandled exception atERRORlevel with structuredextra(previously silent — Python only emitted aDEBUG-level “Task exception was never retrieved”). -
AsyncAgentContextWriter._flush_sessiondone-callback: now includesproduct_idin the log message andextradict for structured log filtering.
Migration notes
AsyncAgentContextWriter + BaseChatbotWriter — avoid double-debouncing
BaseChatbotWriter already coalesces rapid write_actions() calls via its post_link_debounce_s window (default 150 ms). When wiring an AsyncAgentContextWriter to a BaseChatbotWriter subclass, keep debounce_ms=0 (the default):
debounce_ms > 0 only when write_actions points to a raw destination with no internal coalescing (e.g. a direct Zendesk or Salesforce API call).
[0.4.0] — 2026-04-09
Bug fixes
- Fixed TOCTOU race in
RedisEventBuffer._get_redis(): concurrent callers could each create their own connection pool; now serialised withasyncio.Lockand double-checked locking. - Fixed
AsyncSessionSummarizer.flush()cancellation safety: replaced sequentialfor q in queues: await q.join()withasyncio.gather(*[asyncio.shield(q.join()) for q in queues])so all queues are drained even when the caller is cancelled.
Other
- Added
CHANGELOG.mdto document breaking changes and new features going forward.
[0.3.0] — 2026-04-09
Breaking changes
AsyncSessionSummarizer.get_context(session_id)is nowasync— callers mustawaitit.AsyncSessionSummarizer.reset(session_id)is nowasync— callers mustawaitit.AsyncSessionSummarizer.active_sessionsis now anasyncproperty — callers mustawaitit.AsyncSessionSummarizer.add()now returns immediately — the LLM call is dispatched to a background worker queue rather than awaited inline. Code that relied on the LLM having completed by the timeawait add()returned must callawait summarizer.flush()before inspecting state.
New features
AsyncSessionSummarizer.flush()— waits for all queued payloads to be fully processed; cancellation-safe viaasyncio.gather+asyncio.shield.SdkMetricsHookprotocol (autoplay_sdk.metrics) — a@runtime_checkable Protocolthat customers can implement to receive Prometheus / Datadog / OTEL counters for: dropped events, summarizer latency, Redis operation latency, queue depth, and semaphore timeouts.metrics=constructor parameter onConnectorClient,AsyncConnectorClient,AsyncSessionSummarizer, andRedisEventBuffer.initial_backoff_s,max_backoff_s,max_retriesconstructor parameters on both SSE clients — exposes and documents the reconnect policy with configurable jitter-backed exponential backoff.- Per-session ordering guarantee in
AsyncSessionSummarizer— each session now has its ownasyncio.Queue+ backgroundasyncio.Taskworker, ensuring that concurrentadd()calls for the same session are always processed in arrival order, even if an earlier LLM call fails. py.typedmarker — the package is now PEP 561-compliant; static type-checkers will find type stubs automatically.
Bug fixes (v0.2.x → v0.3.0)
The following 24 items were addressed across two audit passes: Concurrency & correctness- Fixed
AsyncSessionSummarizerordering bug: concurrent adds during an LLM failure could produce out-of-orderon_summarycallbacks (replaced single lock with per-session queue). - Fixed TOCTOU race in
RedisEventBuffer._get_redis(): concurrent callers could each create their own connection pool; now serialised withasyncio.Lockand double-checked locking. - Replaced
asyncio.Semaphore._value(private API, breaks across CPython minor versions) with an explicit_TrackedSemaphorecounter. - Replaced
asyncio.get_event_loop()(deprecated) withasyncio.get_running_loop()inapp.pyandasync_client.py. - Replaced
asyncio.ensure_future()withloop.create_task()inasync_client.py. - Added
done_callbackon fire-and-forgetasyncio.Tasks to log unhandled exceptions instead of silently swallowing them.
RedisEventBuffer._payload_from_json()now wrapsjson.loadsintry/except json.JSONDecodeError; corrupt ZSET members no longer crash the drain loop.ConnectorClientnow setsself._running = FalseonKeyboardInterruptso callers can inspect the state after shutdown.- All
exceptclauses that were swallowing exceptions now passexc_info=Trueto the logger so tracebacks appear in structured logs.
RedisEventBufferZSET members now carry a unique UUID prefix, preventing silent deduplication when two events arrive at the same millisecond timestamp.SessionSummarizernow deletes_history[session_id]and_counts[session_id]after summarisation to prevent unbounded memory growth.AsyncConnectorClient._session_semaphoreschanged from plaindicttocollections.OrderedDictwith LRU eviction to cap memory when many short-lived sessions are processed.
- Extracted
LazyRedisClienthelper (storage/_redis.py) so all storage modules share one lazily-initialised, thread-safe Redis client instead of each implementing the same racy pattern. session_storenow usesLazyRedisClientand exposes aSessionState.from_redis_link()classmethod that owns the full reconstruction logic (preventing silent field omissions on restore).SessionStategains anerror: Optional[str]field to surface last-known error reason through the API.
- Replaced custom
_JsonFormatterinapp.pythat ignoredextra={}fields with a correct implementation that merges them into the JSON line. - All structured log calls now use
extra={}dicts consistently. - Metrics instrumentation added at every observability-relevant site: event drops, queue depth, semaphore timeouts, summarizer latency, Redis add/drain latency.
- Added
__all__exports to__init__.pysofrom autoplay_sdk import *is well-defined. - Added
__version__ = "0.3.0"to__init__.py. - Added
py.typedmarker for PEP 561 compliance. - Removed dead
_dropped_count/_total_countmetrics fields that were incremented but never surfaced. - Standardised public API:
on_dropcallback signature is now consistent acrossConnectorClient,AsyncConnectorClient, andRedisEventBuffer.