Back to Updates

Platform Updates — June 2026

ibl.ai EngineeringJune 30, 2026
Application

74 production image releases (1.25.1 → 1.61.1): agent-driven edX course creation, streaming text-to-speech, first-class privacy-mode chat, the agentAI→OS and Skills→LMS renames, principal-agnostic RBAC, and optional AWS WAF in the infra CLI.

Production image releases 1.25.11.61.1 (74 releases, June 2026).

serviceimage / pinversion span
dmiblai-dm-pro4.267.14.294.1-ai
osiblai-os-spa0.70.10.88.2
lmsiblai-lms-spa0.36.00.43.2
edxiblai-edx-prosumac.2.32.0sumac.2.36.0
cliiblai/infra-cli1.11.01.12.0

iblai-dm-pro4.267.14.294.1-ai

Text-to-speech grew up. Chat-message TTS now streams audio from the provider so playback starts before the whole clip is encoded, and messages over 4000 characters are split on sentence boundaries and re-encoded through a single lameenc MP3 encoder into one gapless stream (new lameenc==1.8.2 dependency). Agents gained a voice_instructions field that controls the reading style — passed to OpenAI as the speech instructions (switching synthesis to gpt-4o-mini-tts) and to Google as the synthesis prompt. Google TTS moved to the Gemini TTS model (gemini-3.1-flash-tts-preview), auto-detecting language from the text via langdetect. Message text is HTML-stripped before synthesis via strip_html_tags, which preserves accessibility content (alt, aria-label, title) so it is spoken rather than dropped, and drops aria-hidden="true" elements so screen-reader-hidden content is never read aloud.

Emoji and symbol handling for spoken output was iterated to a stable rule. An initial pass rewrote decorative emoji and symbols into their spoken names before synthesis (new emoji==2.15.0 dependency); a follow-up narrowed this so only emoji that belong to a math equation are kept and spoken (inside delimited LaTeX/markdown maths such as $...$, \(...\), or next to a math operator), while every other decorative emoji is stripped. The unicodedata symbol-name fallback was removed so math symbols (→, ∑, ≈) pass through untouched instead of being mangled into verbose Unicode names. Authorization for TTS is a plain session-ownership check — only the session's student may synthesize its messages (403 otherwise).

Privacy mode became a first-class, fully-featured chat state. The PII privacy router (Mentor.privacy_action) gained an allow option alongside redact/mask/block: detected PII is passed to the LLM unchanged but still written to ModerationLog for audit (migration 0327_alter_mentor_privacy_action; default stays redact). More significantly, DISABLED (privacy-mode) sessions — which previously lost all context between turns because history writes short-circuited — now retain in-session context via an ephemeral, Redis-only conversation history (24h TTL, size-bounded, refreshed each turn, never persisted to Postgres). IBLChatMessageHistory writes append to the cache and all four read paths read from it; get_guided_prompts falls back to it when there are no history rows. Because session-driven privacy severs Session.student (student=None), a series of fixes restored the authenticated owner across the affected surfaces: chat file uploads and the guided-prompts endpoint, artifact reads (via a short-lived username-keyed ephemeral grant cache covering retrieve/versions/version_detail only), TTS for student-less messages (addressed by generation_id, with the /tts/ route converter widened from to ), and voice calls (the call-credentials endpoint now issues credentials for student-less privacy sessions and the voice path skips the per-student cloned-voice lookup). A separate fix made chat privacy mode work with RBAC-enabled non-anonymous agents by falling back to flow.username for policy evaluation.

Per-agent chat-history and RAG tuning replaced hardcoded limits. Three new nullable Mentor columns — chat_history_max_tokens, chat_history_max_messages, and rag_retrieval_k — make retention and main-RAG k configurable per agent (migration 0332_mentor_chat_and_rag_limits), each coalescing NULL to a module default (15000 tokens / 20 messages / k=4) so existing rows are unchanged; the default token cap was raised from the prior hardcoded 2000 to 15000. IBLChatMessageHistory now enforces a dual cap (token budget AND message count) across all four read entry points, resolved per-agent via _resolve_history_caps, and the main-RAG retriever folds the resolved k into search_kwargs via _get_retriever_search_kwargs. Separately, chat-time payload.metadata (product group, state, level, etc.) is now folded into the RAG query string sent to the vector store so client-supplied metadata influences retrieval.

The page-content HTML-stripping behavior converged on a per-agent toggle. Whether the current web page's raw HTML (payload.page_content) reaches the LLM went through several revisions — stop stripping (so inline questions like "what does the Submit button do?" work), then a revert (raw HTML was bloating prompts past the context window), then a tenant-wide STRIP_PAGE_CONTENT_HTML setting with an admin endpoint — before settling on a per-agent Mentor.strip_page_content_html column (migration 0326, default False). The tenant-wide setting, its PageContentConfigView admin endpoint, and serializer were removed; should_strip_page_content_html now resolves the toggle from the session's agent. strip_html was also hardened to preserve and explicitly surface accessibility names ([alt: ...], [aria-label: ...], [title: ...]) while decomposing