Skip to content

Latest commit

 

History

History
781 lines (532 loc) · 101 KB

File metadata and controls

781 lines (532 loc) · 101 KB
sidebar_label 2.0.0 Release Notes
sidebar_position 1
title Version 2.0.0 Release Notes
description Release notes for CrestApps.OrchardCore 2.0.0 — new modules, breaking changes, and migration guide.

Version 2.0.0 Release Notes

Package version: 2.0.0-preview-0001

This is a major release that introduces new modules, an orchestrator-based architecture, Omnichannel Communications, expanded MCP protocol support, and document handling capabilities.


Build and Tooling

  • Gulp minified JavaScript output restored — The asset pipeline now writes the non-minified and minified JavaScript files through separate streams, which restores generated outputs such as ai-chat.min.js when running gulp rebuild or npm run rebuild with the current Gulp toolchain. Copy-based vendor assets such as purify.min.js and chart.min.js continue to be emitted to wwwroot from their manifests.

New Modules

AI Prompt Templates

  • Feature ID: CrestApps.OrchardCore.AI.Prompting
  • Centralized AI system prompt management with file-based discovery, Liquid template rendering, and prompt composition.
  • Standalone CrestApps.AI.Prompting library can be used in any .NET project without Orchard Core.
  • All built-in system prompts (chart generation, search query extraction, title generation, task planning, RAG instructions, tabular batch processing, etc.) have been migrated to reusable .md template files in AITemplates/Prompts/ directories.
  • Extensible parser infrastructure — Markdown front matter parsing ships by default; YAML, JSON, or other formats can be added by implementing IAITemplateParser.
  • JSON compaction — Fenced ```json ``` blocks in templates are automatically compacted during parsing to reduce token usage while keeping source files readable.
  • Template caching — Parsed templates are cached in memory and invalidated on tenant reload or application restart.
  • AITemplateBuilder — High-performance builder class for composing system prompts from multiple templates, raw strings, and template IDs with minimal allocations.
  • Category-grouped UI — AI Profile and Chat Interaction editors show prompt templates grouped by category with description display and JSON key-value parameter input.
  • Parameter descriptors — Templates can declare expected arguments using Parameters: front matter metadata. The UI displays parameter names and descriptions when a template is selected.
  • Runtime template consumption — When a prompt template is selected on an AI Profile or Chat Interaction, the template is rendered at runtime during orchestration. The rendered output replaces the custom system message.
  • Liquid templates with typed objects — Document availability and task planning templates now receive typed .NET objects (e.g., AIToolDefinitionEntry, ToolRegistryEntry, ChatDocumentInfo) and use Liquid loops to render tool and document lists.
  • RAG-related prompts (response guidelines, scope constraints, tool search instructions) extracted from orchestration handlers into reusable template files.
  • IAITemplateService.RenderAsync now throws KeyNotFoundException when the requested template ID is not found. Previously it returned null.
  • IAITemplateRenderer renamed to IAITemplateEngine — The interface handles both rendering and validation, so the name better reflects its responsibilities. FluidAITemplateRenderer is now FluidAITemplateEngine.
  • render_ai_template Liquid tag — New custom Fluid tag for rendering a sub-template within the current Liquid scope. {% render_ai_template "template-id" %} shares parent variables with the included template, enabling composable prompt templates. Variables defined in the sub-template do not leak to the parent. Includes recursion protection (max depth 10). Available in the standalone CrestApps.AI.Prompting library.
  • Removed include_prompt Liquid filter — The include_prompt filter has been removed in favor of the more capable render_ai_template tag. Replace {{ "template-id" | include_prompt }} with {% render_ai_template "template-id" %}.

AI Profile Templates

  • Feature ID: CrestApps.OrchardCore.AI (part of the core AI module)
  • Reusable templates for creating AI Profiles with pre-configured system messages, model parameters, tool selections, and connection settings.
  • Source-aware architecture — Templates are source-aware with a Source property. Two built-in sources are registered: Profile (for AI profile configuration) and SystemPrompt (for reusable system prompt text). Other modules can register additional sources via AIOptions.AddTemplateSource().
  • Metadata-based storage — The AIProfileTemplate model contains only generic fields (Name, DisplayText, Description, Category, IsListable, Source). All source-specific fields are stored as metadata objects in the template's Properties using As<T>()/Put<T>():
    • ProfileTemplateMetadata — Profile type, connection, system message, model parameters, tool names, etc.
    • SystemPromptTemplateMetadata — System prompt text only.
  • Three-driver architecture — Template editing uses source-aware display drivers:
    • AIProfileTemplateFieldsDisplayDriver — Generic fields (always visible for all sources).
    • AIProfileTemplateDisplayDriver — Profile-specific fields (only shown for Profile source).
    • SystemPromptTemplateDisplayDriver — System prompt field (only shown for SystemPrompt source).
  • Triple-source discovery — Define templates via admin UI (database-stored, runtime-managed), as .md files in AITemplates/Profiles/ directories (module-embedded, read-only), or in App_Data/AITemplates/Profiles/ and App_Data/Sites/{tenantName}/AITemplates/Profiles/ (filesystem, local customization).
  • Template application — When creating a new AI Profile, select a template from the dropdown and click Apply to pre-fill the form. All display drivers render the pre-populated values. Template properties (except ProfileTemplateMetadata and SystemPromptTemplateMetadata) are copied to both profile.Properties and profile.Settings, ensuring all profile drivers can read applied values.
  • Generic Templates UI — The admin UI at Artificial Intelligence → Templates shows a source selection modal when creating a new template, similar to the Profiles UI. The controller, views, and admin menu have been renamed from ProfileTemplates to AITemplates for genericity.
  • External module drivers — Every module that adds a DisplayDriver<AIProfile> also provides a DisplayDriver<AIProfileTemplate> so templates capture the full profile configuration. Source-specific drivers use .RenderWhen() to hide sections that don't apply.
    • AI Tools — Select available tools for the template.
    • AI Chat — Admin menu visibility, session settings, data extraction entries, post-session tasks.
    • AI Chat Analytics — Session metrics, conversion goals, AI resolution detection.
    • AI Documents — Allow session documents toggle. Upload and attach documents directly to templates with text extraction, chunking, and embedding generation.
    • AI MCP — MCP connection selections.
    • AI DataSources — Data source, strictness, top N documents, filters.
  • Document cloning — When a template includes attached profile documents, applying the template to a new profile clones all AIDocument and AIDocumentChunk records (including pre-computed embeddings) to the new profile. This allows templates to serve as pre-packaged RAG knowledge bases.
  • AIProfileTemplateIndex — YesSql MapIndex for efficient querying by Source, Name, Category, ProfileType, and IsListable.
  • IAIProfileTemplateManager — Extends INamedSourceCatalogManager<AIProfileTemplate> with GetListableAsync(). Provides full CRUD and unified read access, merging database and file-based templates. Database templates take precedence when names conflict. Replaces the former IAIProfileTemplateService.
  • Deployment support — Export AI Profile Templates via Orchard Core deployment plans. Select all templates or choose specific ones by name.
  • Recipe support — Import and export templates using the AIProfileTemplate recipe step. Supports create, update by ID or name, and validation.
  • Markdown front matter — Profile templates use the same parser as prompt templates. Profile-specific keys (ProfileType, ConnectionName, Temperature, ToolNames, etc.) are extracted from AdditionalProperties.
  • Permissions — New ManageAIProfileTemplates permission controls access to template management.
  • Sample templates — Ships with chat-session-summarizer.md (TemplatePrompt profile for summarizing conversations) as a built-in example template.
  • Recipe step schemas — When the CrestApps.OrchardCore.Recipes feature is enabled, JSON schemas are registered for all AI recipe steps (AIProfile, AIProfileTemplate, AIDeployment, DeleteAIDeployments, AIProviderConnections) providing validation and documentation for recipe authoring.
  • Extensible — Custom modules can add DisplayDriver<AIProfileTemplate> to contribute template settings. See the profile templates documentation.

AI Chat Interactions

  • Feature ID: CrestApps.OrchardCore.AI.Chat.Interactions
  • Provides ad-hoc chat sessions with configurable parameters — users can adjust model settings, attach documents, upload images, and generate charts without requiring a predefined AI profile.

AI Chat Copilot Integration

  • Feature ID: CrestApps.OrchardCore.AI.Chat.Copilot
  • Integrates with the GitHub Copilot SDK to provide a Copilot-based orchestrator for AI completions.

AI Documents

A suite of modules for document upload, text extraction, and embedding during chat sessions:

Module Feature ID
AI Documents (Core) CrestApps.OrchardCore.AI.Documents
PDF Support CrestApps.OrchardCore.AI.Documents.Pdf
OpenXml Support (Word, Excel, PowerPoint) CrestApps.OrchardCore.AI.Documents.OpenXml
Azure AI Search CrestApps.OrchardCore.AI.Documents.AzureAI
Elasticsearch CrestApps.OrchardCore.AI.Documents.Elasticsearch
  • Updated AI Documents settings guidance to use the AI Documents index naming, include indexing-feature enablement guidance, and document production guidance for index profile stability.
  • Updated AI Profile document orchestration prompts and document-search output so profile-attached documents are treated as hidden background knowledge, while session-uploaded documents remain user-visible.
  • Updated document availability prompt arguments to split profile knowledge-base documents from user-supplied session documents for clearer privacy-aware instructions.

AI Data Sources

Modules for RAG (Retrieval-Augmented Generation) and knowledge base indexing:

Module Feature ID
AI Data Sources (Core) CrestApps.OrchardCore.AI.DataSources
Azure AI Search CrestApps.OrchardCore.AI.DataSources.AzureAI
Elasticsearch CrestApps.OrchardCore.AI.DataSources.Elasticsearch

MCP Resource Adapters

Module Feature ID
FTP Resources CrestApps.OrchardCore.AI.Mcp.Resources.Ftp
SFTP Resources CrestApps.OrchardCore.AI.Mcp.Resources.Sftp

Agent-to-Agent (A2A) Protocol

A new module implementing the A2A protocol for multi-agent interoperability:

Feature Feature ID Description
A2A Client CrestApps.OrchardCore.AI.A2A Connect to external A2A hosts and use their agents as AI tools in orchestration.
A2A Host CrestApps.OrchardCore.AI.A2A.Host Expose Agent-type AI Profiles as discoverable A2A agents at /.well-known/agent-card.json.
  • A2A Host — Exposes all Agent-type AI Profiles as A2A agents. Each agent gets its own agent card with skills derived from the profile description and configured tools. Supports streaming (TaskArtifactUpdateEvent SSE) and non-streaming responses. Configurable authentication: OpenId Connect (default, integrates with OrchardCore's OpenIddict feature), API Key, or None. Agent cards include SecuritySchemes so clients know how to authenticate. An ExposeAgentsAsSkill option consolidates all agents into a single card with multiple skills.
  • A2A Client — Add connections to external A2A hosts from the admin UI (Artificial Intelligence → Agent to Agent Hosts). Connections support the same authentication types as MCP client connections (Anonymous, API Key, Basic, OAuth 2.0, etc.). Agent cards are cached for 15 minutes per connection with signal-based invalidation on update/delete. Select A2A connections on AI Profiles, Templates, and Chat Interactions under the Capabilities tab. Connected agents are automatically registered as AI tools via A2AAgentProxyTool and can be invoked by the orchestrator.
  • AI Discovery Functions — Three new built-in AI tools: list_available_agents (returns agent cards from all connections and local agents), find_agent_for_task (semantic + keyword search for the best agent), and find_tools_for_task (semantic + keyword search for relevant AI tools). These help the AI model discover and select the right agent or tool for a given task.
  • Sample A2A Client — A standalone Razor Pages application at CrestApps.OrchardCore.Samples.A2AClient for testing A2A agents. Lists agent cards, supports streaming and non-streaming message sending, and integrates with .NET Aspire.

Omnichannel Communications

A new suite of modules for multi-channel communication:

Module Feature ID Description
Omnichannel (Core) CrestApps.OrchardCore.Omnichannel Core omnichannel services
SMS CrestApps.OrchardCore.Omnichannel.Sms AI-driven SMS automation with Twilio
Event Grid CrestApps.OrchardCore.Omnichannel.EventGrid Azure Event Grid webhook integration
Management CrestApps.OrchardCore.Omnichannel.Managements Contact and conversation management

Recipes Module

  • Feature ID: CrestApps.OrchardCore.Recipes
  • Provides recipe steps for configuring CrestApps modules via recipes.

New Features in Existing Modules

AI Services (CrestApps.OrchardCore.AI)

  • Orchestrator Architecture — The new IOrchestrator interface replaces the previous prompt routing system. The orchestrator manages planning, tool scoping, and iterative agent execution loops.
  • AI Tool Registration — New fluent API for registering AI tools with .AddAITool<T>(), supporting categories, purposes, and selectable/system tool modes.
  • AI Profile Types — Added Utility and TemplatePrompt profile types in addition to Chat.
  • AI Deployments — New feature for managing AI model deployments.
  • Typed AI Deployments — AI deployments are now first-class typed entities. Each deployment has a Type property (Chat, Utility, Embedding, Image, SpeechToText) and an IsDefault flag. Connections are pure connections — deployment name fields (ChatDeploymentName, EmbeddingDeploymentName, UtilityDeploymentName, ImagesDeploymentName) on AIProviderConnection are deprecated. The IAIDeploymentManager service resolves deployments by type with fallback chain (explicit → connection default → global default). New settings page under Settings > Artificial Intelligence > Default Deployments to configure global defaults. AI Profiles and Chat Interactions now use ChatDeploymentId and UtilityDeploymentId instead of DeploymentId. Deployment dropdowns show all deployments grouped by connection. Existing connection deployment names are automatically migrated to typed AIDeployment records on startup. Both old format (deployment names on connection) and new format (Deployments array) are supported in appsettings.json. See the migration guide for details.
  • AI Connection Management — UI for managing provider connections from the admin dashboard.
  • AI Chat WebAPI — RESTful API endpoints for interacting with AI chat.
  • Chat Mode (Voice Input & Output) — AI Chat and Chat Interactions now support a unified Chat Mode dropdown with three options: Text Only (default), Audio Input (microphone button for speech-to-text dictation), and Conversation (two-way voice interaction with auto-send and text-to-speech). Users can click a microphone button to record speech, which is streamed to the server via SignalR and transcribed using the configured ISpeechToTextClient (e.g., OpenAI Whisper, Azure OpenAI Whisper). The transcribed text appears in the input field for review before sending. For AI Chat, configure via the Chat Mode dropdown on AI Profiles or AI Profile Templates (visible only for Chat profile types). For Chat Interactions, configure via the site-level Chat Mode dropdown under Settings → Artificial Intelligence → Chat Interactions. Audio Input requires a Default Speech-to-Text Deployment; Conversation requires both STT and TTS default deployments.
  • Workflow Integration — AI Completion tasks for Orchard Core Workflows.
  • Extensible Chat Response Handlers — Chat prompts are now routed through a pluggable IChatResponseHandler abstraction. The built-in AI handler remains the default, but custom handlers can be registered to route prompts to external systems (e.g., live agent platforms like Genesys). Handlers support two modes: streaming (immediate response like AI) and deferred (response arrives later via webhook). Sessions and interactions have a ResponseHandlerName property that can be changed mid-conversation by AI functions for live agent handoff scenarios. AI Profiles and Templates support an Initial Response Handler setting to bypass AI entirely. The chat UI shows a handler selector when multiple handlers are registered. Custom handlers are not supported in Conversation mode — the resolver always returns the AI handler when ChatMode.Conversation is active because conversation mode requires the AI pipeline for speech-to-text and text-to-speech. See the Response Handlers documentation for details.
  • Response handler documentation corrected — The deferred webhook examples no longer tell integrators to call SendAsync("ReceiveMessage", ...), because there is no built-in ReceiveMessage SignalR client method in the current AI Chat or Chat Interaction UI. The docs now show the supported message-delivery APIs: persist the assistant reply, then either append it with ReceiveConversationAssistantToken / ReceiveConversationAssistantComplete or reload the transcript with LoadSession / LoadInteraction. Transient system messages such as typing, transfer, and agent-connected updates still use IChatNotificationSender. This is a documentation correction only; no runtime behavior changed.
  • Existing chat connections now rejoin deferred-response groups on startup — When an AI Chat page or Chat Interaction page opened with an existing session or interaction already rendered, the browser could display the existing transcript without calling LoadSession or LoadInteraction. That meant the active SignalR connection never joined the corresponding group, so deferred webhook replies were persisted but not shown in real time until the user sent another message. The built-in JavaScript clients now automatically call LoadSession(existingSessionId) or LoadInteraction(existingItemId) on startup so the current connection joins the proper group immediately and deferred external replies can arrive live.
  • External Chat Relay — New protocol-agnostic infrastructure for real-time bidirectional communication with third-party live-agent platforms. Unlike the webhook pattern (where the external system calls back into your application), an external chat relay maintains a persistent connection so events like typing indicators, agent-connected notifications, wait-time updates, connection-status signals, and messages flow instantly without polling. The relay interface (IExternalChatRelay) is transport-agnostic — implementations can use WebSocket, SSE, gRPC streaming, WebRTC data channels, message queues, event buses, or any other protocol. Key abstractions: IExternalChatRelay (persistent connection interface with SendPromptAsync for user→external, SendSignalAsync for feedback signals, and IsConnectedAsync() for status checks), IExternalChatRelayManager (singleton lifecycle manager), IExternalChatRelayEventHandler (event-to-notification router using keyed builder/handler pattern). Event types are strings (ExternalChatRelayEventTypes constants) for extensibility — custom event types are supported out of the box. Built-in event types include: agent-typing, agent-stopped-typing, agent-connected, agent-disconnected, agent-reconnecting, connection-lost, connection-restored, message, wait-time-updated, session-ended. The default handler resolves keyed IExternalChatRelayNotificationBuilder services per event type — each builder declares a NotificationType (used to create the notification) and populates properties via Build. The IExternalChatRelayNotificationHandler supports send, update, and remove operations. To add custom event types, register a keyed builder: services.AddKeyedScoped<IExternalChatRelayNotificationBuilder, MyBuilder>("my-event"). See the Response Handlers documentation for a complete implementation example.
  • Chat UI Notifications — New extensible notification system that allows C# code to send transient system messages to the chat interface via SignalR — no JavaScript required. Built-in notifications include typing indicators ("Mike is typing…"), transfer status with estimated wait times and cancel buttons, agent-connected indicators, and conversation/session ended indicators. The IChatNotificationSender interface provides SendAsync, UpdateAsync, and RemoveAsync methods. Well-known notification types are available via ChatNotificationTypes and action names via ChatNotificationActionNames. Notifications are created using new ChatNotification("type") with the Type serving as the sole identifier — e.g., new ChatNotification(ChatNotificationTypes.Typing). All user-facing strings accept IStringLocalizer for full localization support. ChatNotification requires a type parameter in its constructor and the Type setter is private. Notification system messages support action buttons that trigger server-side IChatNotificationActionHandler callbacks (registered as keyed services). Built-in action handlers: cancel-transfer (resets handler to AI) and end-session (closes the session). The system uses an extensible transport architecture — IChatNotificationTransport implementations are registered as keyed services by ChatContextType, allowing third-party modules to add notification support for custom hubs. Custom notification types with custom actions and styling are fully supported. See the Chat UI Notifications documentation for details.
  • SpeechTextSanitizer Utility — Extracted SanitizeForSpeech from both hub classes into a shared SpeechTextSanitizer.Sanitize() static method in CrestApps.OrchardCore.AI.Core.Services. This utility strips markdown formatting, code blocks, emoji, and other non-speech elements from text before passing it to a text-to-speech engine. Available for reuse by any module.
  • CrestApps.OrchardCore.AI.Chat.Core Library — Introduced a new core library (src/Core/CrestApps.OrchardCore.AI.Chat.Core) containing AIChatHub, ChatInteractionHub, and shared hub infrastructure. Both hubs now inherit from a common ChatHubBase<TClient> base class that provides shared text-to-speech streaming, conversation stop, and sentence-level speech synthesis methods. Client interfaces (IAIChatHubClient, IChatInteractionHubClient) extend a shared IChatHubClient interface. External modules can reference the Core library to resolve IHubContext<AIChatHub> or IHubContext<ChatInteractionHub> for sending deferred messages without depending on the module projects directly. Static helper methods AIChatHub.GetSessionGroupName() and ChatInteractionHub.GetInteractionGroupName() are public for use in webhook endpoints.

MCP (CrestApps.OrchardCore.AI.Mcp)

  • MCP Server — Expose your Orchard Core site as an MCP server endpoint, allowing external AI agents to discover and use your tools, prompts, and resources.
  • MCP Prompts and Resources — Prompts and resources can be added and managed via the admin UI.
  • Templated Resources — Support for dynamic MCP resources defined with URI templates.
  • Stdio Transport — Connect to local MCP servers (e.g., Docker containers) via Standard Input/Output.
  • Template URI Whitespace Handling — Resource URI templates and incoming URIs are now trimmed of leading/trailing whitespace before matching, preventing mismatches caused by accidental spaces in URI definitions.
  • File Resource Directory Rejection — The file resource handler now returns a descriptive error when the resolved path is a directory instead of a file, rather than attempting to read directory content.

AI Agent (CrestApps.OrchardCore.AI.Agent)

  • Expanded toolset with 30+ built-in tools covering content management, tenant management, feature toggles, workflow automation, and communication tasks.
  • Removed per-tool permission checks — AI tools no longer perform their own authorization checks at invocation time. Permission enforcement is handled at the profile design level by LocalToolRegistryProvider, which verifies that the user configuring the AI Profile has AIPermissions.AccessAITool permission for each tool they expose. This ensures tools work correctly in anonymous contexts (e.g., public chat widgets, background tasks, post-session processing) without failing due to missing user authentication.
  • CreateOrUpdateContentTool — Owner fallback parameters — Added optional ownerUsername, ownerUserId, and ownerEmail parameters. When content is created without an authenticated user (e.g., from an anonymous chat widget), the AI model can specify who the content should be created on behalf of. The tool resolves the user and sets contentItem.Owner and contentItem.Author accordingly.

Breaking: If you relied on individual AI tools rejecting unauthorized requests at runtime (e.g., IsAuthorizedAsync returning permission-denied messages), this behavior has been removed. All authorization is now enforced at the tool registry level when an AI Profile or Chat Interaction is configured. Ensure your AI Profiles are configured by users with appropriate permissions.


Improvements

AI Playwright Browser Automation (CrestApps.OrchardCore.AI.Playwright)

  • Persistent multi-step browser sessions — Playwright browser windows now remain open across follow-up tasks instead of closing immediately after each completed assistant reply.

  • Inactivity-based cleanup — Inactive Playwright browser sessions are now closed by a background cleanup task using the configured profile session timeout when available, or a 30 minute default timeout.

  • Interactive page observation tools — Added deterministic live-page tools for playwright_get_page_content, playwright_find_element, playwright_check_element_exists, playwright_get_visible_widgets, and playwright_take_screenshot.

  • Interactive completion flow — The Playwright operator prompt now tells the assistant to ask whether the user has another task after each completed workflow and to answer page-observation questions from the live browser state instead of guessing.

  • Row-scoped content list editing — Content item list actions now resolve the row that contains the requested title before invoking row-level actions such as Edit, avoiding misclicks when multiple items are visible.

  • Smart HtmlBody editor detection — playwright_fill_by_label now detects visible textareas, hidden source textareas, rich text surfaces, contenteditable editors, and iframe-backed editors for fields such as HtmlBody.

  • Append-first body editing — Body-like fields now append new text by default instead of overwriting existing content unless the user explicitly asks to replace the content.

  • Stricter completion reporting — The Playwright operator prompt now instructs the assistant to avoid conflicting success and failure statements in the same reply and to report save or publish success only from the latest verified page observation.

  • Visible content list skills — Added playwright_list_content_items and playwright_open_content_item_editor so the assistant can stay on the current Orchard content list, report visible items directly, and open an editor by title without restarting from admin home.

  • Visible action indicator — The dedicated Playwright browser now shows an in-page highlight ring and label before clicks and typing so users can see what the operator is targeting.

  • Current-page-first editing flow — The Playwright operator now opens a clear visible content-item match directly, stays on the current page for follow-up requests, and avoids auto-saving after a field edit unless the user explicitly asks.

  • Clearer Orchard failure reporting — Save and publish flows now surface application error headlines such as database is locked from the returned Orchard page state, and Playwright tool failures are returned as structured results instead of raw tool exceptions.

Configuration-Based AI Deployments

  • appsettings.json deployments — AI deployments defined in configuration (both connection-based Deployments arrays and the new standalone CrestApps_AI:Deployments array) are now automatically available at runtime. This allows site owners to define deployments in appsettings.json so they are shared across all tenants without per-tenant configuration.
  • Non-connection deployments via config — Contained-connection providers (e.g., Azure Speech) can now be configured in appsettings.json as standalone deployment objects with a ProviderName, enabling speech-to-text and other contained-connection deployments to be defined centrally rather than through the admin UI on each tenant.
  • Read-only and ephemeral — Configuration-sourced deployments appear in dropdown menus and API queries alongside database-managed deployments, but are not persisted to the database. Removing them from configuration removes them from the system.
  • Deterministic IDs — Configuration deployments receive stable, deterministic IDs derived from their provider, connection, and deployment names, ensuring consistent references across application restarts.

MCP Client Authentication (CrestApps.OrchardCore.AI.Mcp)

  • Structured Authentication Types — The SSE connection UI now provides a dedicated authentication type selector instead of requiring raw HTTP header JSON. Supported types: Anonymous, API Key, Basic Authentication, OAuth 2.0 Client Credentials, OAuth 2.0 + Private Key JWT, OAuth 2.0 + Mutual TLS (mTLS), and Custom Headers (legacy).
  • API Key Authentication — Configure an API key with a customizable header name and optional prefix (e.g., Bearer, ApiKey).
  • Basic Authentication — Provide username and password for HTTP Basic auth. Credentials are Base64-encoded automatically.
  • OAuth 2.0 Client Credentials — Acquire access tokens automatically using the client_credentials grant type. Tokens are cached in memory and refreshed before expiration.
  • OAuth 2.0 + Private Key JWT — Authenticate to OAuth 2.0 token endpoints using a signed JWT client assertion with an RSA private key. Supports optional Key ID (kid) for identity providers that require it.
  • OAuth 2.0 + Mutual TLS (mTLS) — Authenticate using a PFX/PKCS#12 client certificate for mutual TLS authentication with the token endpoint.
  • Credential Protection — All sensitive fields (API keys, passwords, client secrets, private keys, client certificates, certificate passwords) are encrypted at rest using ASP.NET Core Data Protection. Encrypted values are never exposed in deployment exports.
  • Backward Compatibility — Existing connections with raw AdditionalHeaders are automatically recognized as "Custom Headers" type when editing.

Breaking: IMcpClientTransportProvider.Get() has been renamed to GetAsync() and now returns Task<IClientTransport> instead of IClientTransport. Custom transport provider implementations must update their method signature.

Unified Citation & Reference System

The citation and reference system has been completely reworked so that every AI provider (Azure OpenAI, OpenAI, Ollama, Azure AI Inference) now returns the same citation references. Previously, citations only worked with Azure OpenAI's native data-sources feature (GetMessageContext()); since we now inject context ourselves via preemptive RAG and tool-based search, that approach no longer applied.

What changed:

  • [doc:N] citation markers are now produced consistently by both Data Source and Document preemptive RAG handlers, as well as by the DataSourceSearchTool and SearchDocumentsTool AI tools.
  • referenceType is stored in the knowledge base index so the system knows whether a reference is a Content item, an uploaded Document, or a custom data source type.
  • AICompletionReference now includes ReferenceId and ReferenceType properties, enabling downstream consumers (hubs, UI) to generate appropriate links.
  • IAIReferenceLinkResolver — a new keyed-service interface for resolving reference links by type. Register custom resolvers with services.AddKeyedScoped<IAIReferenceLinkResolver, MyResolver>("MyType") to generate links for custom reference types.
  • CompositeAIReferenceLinkResolver dispatches to the correct keyed resolver based on referenceType. When no resolver is registered, the reference is shown without a link.
  • CitationReferenceCollector collects references from all sources (preemptive RAG context, tool-invoked searches) and resolves links in a single pass.
  • Content item link resolutionDefaultAILinkGenerator is registered as a keyed IAIReferenceLinkResolver for the "Content" reference type. Content item references automatically receive links generated via OrchardCore's LinkGenerator with the standard OrchardCore.Contents route. Document references (uploaded files) are shown by filename without a link.
  • DocumentChunkSearchResult now includes DocumentKey and FileName properties for uploaded document citation tracking.
  • Azure OpenAI: Removed the deprecated GetMessageContext() / Citations extraction logic and the IAILinkGenerator dependency from AzureOpenAICompletionClient. References are now handled uniformly via the orchestration pipeline.
  • AIInvocationScope / AIInvocationContext — new AsyncLocal<T>-based ambient context that replaces HttpContext.Items for all per-invocation AI data. This ensures full isolation between concurrent SignalR hub calls on the same WebSocket connection, preventing reference leaks, stale data source IDs, and other cross-invocation contamination issues. See the AI Tools documentation for details.
  • Shared reference counterAIInvocationContext.NextReferenceIndex() provides a monotonically increasing, thread-safe counter used by all preemptive RAG handlers and search tools, ensuring [doc:N] indices never collide even when data source and document references are produced in the same request.
  • Incremental citation delivery — Citation references are now collected and sent to the client progressively during streaming. Preemptive RAG references (from data sources and documents) are resolved before the streaming loop starts, so the first chunk already includes them. Tool-invoked references are merged incrementally during streaming as tools execute. This ensures the JavaScript client can render [doc:N] superscripts in real-time.
  • ChatSession moved to Items dictionaryAIInvocationContext.ChatSession property has been removed. The chat session is now stored in AIInvocationContext.Items["AIChatSession"] instead. Tools that need the chat session should read from Items (e.g., invocationContext.Items.TryGetValue("AIChatSession", out var session)).
  • IsInScope evaluation moved to orchestrator — The IsInScope constraint is no longer evaluated by individual preemptive RAG handlers (DataSourcePreemptiveRagHandler, DocumentPreemptiveRagHandler). Instead, the PreemptiveRagOrchestrationHandler evaluates it after all handlers have run. When no references are produced across all sources and IsInScope is enabled, a scoping directive is injected. When tools are available, the directive encourages the model to try tool-based search before concluding no answer exists, allowing search_data_source and search_documents to discover relevant content that the initial preemptive search missed.
  • Tool-search instructions when preemptive RAG is disabled — When preemptive RAG is disabled but data sources or documents are attached, the orchestrator now injects system-message instructions guiding the model to call search tools (search_data_source, search_documents) to retrieve internal knowledge. When IsInScope is enabled, the model is forced to use only tool-retrieved content and must refuse to answer from general knowledge. When IsInScope is disabled, the model is instructed to try the search tools first and may supplement with general knowledge only if no relevant results are found.
  • RAG text normalization — Content and titles are now normalized before chunking and embedding using RagTextNormalizer. HTML tags, Markdown formatting, escaped HTML entities, and extraneous whitespace are stripped to produce clean plain text. This improves embedding quality, reduces token usage when injecting context into prompts, and prevents raw HTML from leaking into reference titles and chat UI. Normalization uses Microsoft.Extensions.DataIngestion.Markdig for structured Markdown-to-plain-text conversion, combined with HTML tag stripping and entity decoding. Titles in citation references are also normalized at creation time as a defense-in-depth measure for existing indexed data.
  • Token-aware chunking — The custom character-based text chunking has been replaced with DocumentTokenChunker from Microsoft.Extensions.DataIngestion. This uses actual LLM tokenizers (GPT-4o o200k_base) to split content at token boundaries with configurable overlap, producing chunks that align better with embedding model token limits.
  • IngestionDocumentReader-based document parsing — The custom IDocumentTextExtractor interface has been replaced with Microsoft.Extensions.DataIngestion.IngestionDocumentReader, the standard abstraction from Microsoft.Extensions.DataIngestion. Each document module now provides an IngestionDocumentReader implementation registered as a keyed singleton by file extension. The built-in MarkdownReader from Microsoft.Extensions.DataIngestion.Markdig is used for Markdown files. Custom PDF and OpenXml readers extend IngestionDocumentReader following the same patterns used in Microsoft's AI templates. Use services.AddIngestionDocumentReader<T>(extensions) to register custom readers — this replaces the previous AddDocumentTextExtractor<T>() method.
  • Inline citation markers — The system prompt now instructs the AI model to include [doc:N] reference markers inline in its response text, immediately after the relevant statement. This enables users to see which statements are sourced from which references.
  • Context-gated system tools — The search_data_sources and document processing tools (search_documents, list_documents, read_document, read_tabular_data) are now conditionally included in the tool registry based on context availability. search_data_sources is only available when a data source is attached to the AI profile or chat interaction. Document processing tools are only available when documents are attached to the session. This prevents the AI model from seeing tools it cannot use, eliminating hallucinated tool calls and reducing token overhead. The SystemToolRegistryProvider checks AICompletionContext.DataSourceId for data sources and AICompletionContextKeys.HasDocuments in AdditionalProperties for documents.
  • Data source deletion cleanup — When a data source is deleted, all associated document chunks are now properly removed from the master knowledge base index via IDataSourceVectorSearchService.DeleteByDataSourceIdAsync. Elasticsearch uses native DeleteByQuery; Azure AI Search uses filter-based pagination with batch deletion. The cleanup runs as a background job after the HTTP response completes to avoid blocking the admin UI. Previously, DeleteDataSourceDocumentsAsync resolved the document index manager but never invoked any deletion.
  • Chat session document cleanup — When an AI chat session is deleted (single or bulk), all uploaded session documents are now cleaned up: AIDocument records are deleted from the document store, and their vector index chunks are removed via a deferred task from all AI document index profiles. Previously, session deletion only removed the session record, leaving orphaned documents and index entries.
  • Chat interaction document cleanup — When a chat interaction is deleted, the AIDocument records associated with it are now deleted from the document store in addition to the vector index chunk cleanup that was already in place. Previously, only the index chunks were removed, leaving the document store records orphaned.
  • Strengthened tool-search instructions — When preemptive RAG is disabled and data sources or documents are attached, the system prompt now uses mandatory language (MUST call... BEFORE generating any response) to ensure the AI model calls search tools before answering. Previously, advisory language was used which some models ignored. When IsInScope is off, the model is instructed to search first and may fall back to general knowledge only if no results are found.
  • Standardized prompt instruction format — All AI system prompts now use a consistent [Section Header] + numbered rules format across the codebase. This includes [Rules] for utility prompts (chart generation, data extraction, search query extraction, post-session analysis, tabular processing), [Output Format] for expected output examples, and the existing [Scope Constraint], [Knowledge Source Instructions], and [Response Guidelines] for RAG-related prompts.
  • Sequential reference display indices — The JavaScript clients (ai-chat.js, chat-interaction.js) now remap cited reference indices to a sequential 1-based sequence when rendering. If the model cites [doc:2] and [doc:5] but not [doc:1], the user sees superscripts 1 and 2 (not 2 and 5), with the reference list numbered accordingly. This prevents confusing gaps in visible numbering. The remapping uses a two-phase placeholder approach to avoid collisions during index substitution.

Breaking: If you relied on chunk.AdditionalProperties["ContentItemIds"] or chunk.AdditionalProperties["References"] being set on streaming chunks by the Azure OpenAI provider, these are no longer set on individual chunks. References are now collected progressively during streaming from the orchestration context and tool execution context.

Breaking: If you wrote custom AI tools that read AIToolExecutionContext from HttpContext.Items[nameof(AIToolExecutionContext)], update them to use AIInvocationScope.Current?.ToolExecutionContext instead. Similarly, if you read HttpContext.Items["DataSourceId"] or HttpContext.Items["ToolSearchReferences"], these are now on AIInvocationScope.Current.DataSourceId and AIInvocationScope.Current.ToolReferences respectively.

Breaking: If you accessed AIInvocationContext.ChatSession, use AIInvocationScope.Current?.Items["AIChatSession"] instead.

Breaking: The IDocumentTextExtractor interface has been removed. If you implemented a custom document text extractor, migrate it to an IngestionDocumentReader subclass and register it with services.AddIngestionDocumentReader<T>(extensions) instead of AddDocumentTextExtractor<T>(extensions). The reader's ReadAsync method returns an IngestionDocument instead of a raw string — the document processing service extracts text from the IngestionDocument automatically.

Note: Data sources and documents indexed before this release may contain raw HTML or Markdown in their content and titles. To benefit from normalization, re-index your data sources after upgrading.


Performance: Reduced String Allocations with ZString

  • Adopted ZString — Replaced System.Text.StringBuilder with ZString's Utf16ValueStringBuilder across AI system message generation, RAG context building, tool summaries, streaming response accumulation, CSV export, and batch processing. ZString uses ArrayPool<char> pooled buffers instead of allocating new internal arrays, significantly reducing GC pressure in hot paths.
  • Key areas converted: DefaultMcpMetadataPromptGenerator, DefaultOrchestrator, CopilotOrchestrator, DocumentOrchestrationHandler, DocumentPreemptiveRagHandler, DataSourcePreemptiveRagOrchestrationHandler, SearchDocumentsTool, DataSourceSearchTool, TabularBatchProcessor, AIChatHub, ChatInteractionHub, ChatAnalyticsController, ApiAICompletionEndpoint, GenerateImageTool, and others.
  • Benchmark project added — A new BenchmarkDotNet project at tests/CrestApps.OrchardCore.Benchmarks measures the allocation difference across five representative scenarios (system message generation, RAG context, streaming, CSV export, tool summaries). Run with dotnet run -c Release --project tests/CrestApps.OrchardCore.Benchmarks.

Note: Code that passes StringBuilder parameters to DefaultMcpMetadataPromptGenerator.AppendParameterSummary must update to ref Utf16ValueStringBuilder. The Utf16ValueStringBuilder is a disposable struct — always use using var sb = ZString.CreateStringBuilder(); to return pooled buffers.


Performance: AI Chat Session Prompt Storage Separation

  • Prompts moved to a dedicated document storeAIChatSessionPrompt objects are now stored as separate YesSql documents instead of being embedded in the AIChatSession document. This dramatically reduces the data loaded when listing sessions (admin pages, widgets, API), since only session metadata (title, sessionId, profileId, status) is fetched without loading potentially large prompt histories.
  • Index-only session listingsDefaultAIChatSessionManager.PageAsync now uses QueryIndex<AIChatSessionIndex> instead of Query<AIChatSession, AIChatSessionIndex>, returning lightweight AIChatSessionEntry DTOs. This is especially impactful for the admin widget which runs on every admin page request.
  • New IAIChatSessionPromptStore — A new store interface (IAIChatSessionPromptStore) provides GetPromptsAsync, DeleteAllPromptsAsync, and CountAsync operations for session prompts.
  • Automatic data migration — Existing sessions are automatically migrated: prompts are extracted from legacy AIChatSession documents into separate AIChatSessionPrompt documents via a batched deferred task (50 sessions per batch). No manual intervention is required.
  • ChatMessageCompletedContext.Prompts — The handler context now includes a Prompts property with the loaded prompts, so IAIChatSessionHandler implementations can access prompts without an additional store query.

Breaking: AIChatSession.Prompts property has been removed. Code that previously accessed session.Prompts must now use IAIChatSessionPromptStore.GetPromptsAsync(sessionId) to load prompts.

Breaking: AIChatSessionPrompt now extends CatalogItem. The Id property has been replaced by ItemId (inherited from CatalogItem), and a SessionId property has been added to associate prompts with their session.

Breaking: AIChatSessionResult.Sessions now returns IEnumerable<AIChatSessionEntry> instead of IEnumerable<AIChatSession>. AIChatSessionEntry is a lightweight DTO containing only SessionId, ProfileId, Title, UserId, ClientId, Status, CreatedUtc, and LastActivityUtc.

Breaking: PostSessionProcessingService.ProcessAsync and DataExtractionService.ProcessAsync now require an IReadOnlyList<AIChatSessionPrompt> parameter. Callers must load prompts from the store and pass them explicitly.

Breaking: AIChatSessionEventService.RecordSessionEndedAsync now takes an int promptCount parameter instead of reading from session.Prompts.Count.


Update: Initial Prompt Session Start Behavior

WelcomeMessage is now treated as UI placeholder text for new chat sessions and is no longer prepended to model conversation history. A new profile option allows adding an initial prompt that creates the first assistant chat-history message immediately when a new session starts.


Fix: Chat Session Handler Lifecycle Events

IAIChatSessionHandler now extends ICatalogEntryHandler<AIChatSession>, adding full lifecycle events: InitializingAsync, InitializedAsync, CreatingAsync, CreatedAsync, LoadedAsync, DeletingAsync, DeletedAsync, UpdatingAsync, UpdatedAsync, ValidatingAsync, and ValidatedAsync. A new AIChatSessionHandlerBase base class (extending CatalogEntryHandlerBase<AIChatSession>) provides virtual no-op implementations for all lifecycle methods plus the existing MessageCompletedAsync, so handler implementations only need to override the events they care about.

DefaultAIChatSessionManager no longer depends on IAIDocumentStore directly. Instead, it invokes DeletingAsync/DeletedAsync lifecycle events on all registered IAIChatSessionHandler implementations when sessions are deleted. Document cleanup is now handled by a dedicated AIChatSessionDocumentCleanupHandler registered in the AI Documents feature, resolving a dependency injection exception when the AI Documents feature was not enabled.

Breaking: IAIChatSessionHandler now inherits from ICatalogEntryHandler<AIChatSession>. Existing implementations should extend AIChatSessionHandlerBase instead of implementing the interface directly to avoid having to implement all lifecycle methods.


Fix: OpenXml Excel Data Extraction

Fixed OpenXmlIngestionDocumentReader.GetCellValue to correctly handle Excel cells stored as inline strings (InlineString cell type) and boolean values. Previously, inline string cells returned empty text because the code only checked CellValue, which is null for inline strings — the text is stored in the InlineString element instead. Shared string table lookup was also changed from LINQ ElementAtOrDefault to direct ChildElements indexer for O(1) access.


Refactor: Unified DataSourceMetadata Type

AIProfileDataSourceMetadata and ChatInteractionDataSourceMetadata have been merged into a single DataSourceMetadata type in CrestApps.OrchardCore.AI.Models. Both types were identical (containing only a DataSourceId property) and served the same purpose on different entity types. Data migrations automatically rename the stored JSON property keys from the legacy names to DataSourceMetadata.

Breaking: AIProfileDataSourceMetadata (from CrestApps.OrchardCore.AI.Core.Models) and ChatInteractionDataSourceMetadata (from CrestApps.OrchardCore.AI.Chat.Interactions) have been removed. Use DataSourceMetadata (from CrestApps.OrchardCore.AI.Models) instead.


Performance: Separated Document Chunk Storage

  • Document chunks are now stored as individual recordsAIDocument no longer stores chunks (with embeddings) inline. Each text chunk is a separate AIDocumentChunk record in YesSql, linked by AIDocumentId. This eliminates the SQL Server nvarchar(MAX) overflow problem that occurred with large documents (100+ chunks × 1536-dim embeddings ≈ 1.5–5+ MB of JSON per document).
  • Embeddings are cached in YesSql — Each AIDocumentChunk stores its Embedding (float[]) alongside the Content. Embeddings are generated once during document processing and reused during vector index rebuilds, avoiding repeated calls to the embedding API. If an embedding is unavailable (e.g., older data migrated before this change), the chunk is indexed without a vector.
  • New IAIDocumentChunkStore — Provides GetChunksByAIDocumentIdAsync, GetChunksByReferenceAsync, and DeleteByDocumentIdAsync for managing chunks independently of the parent AIDocument.
  • New AIDocumentChunkIndex — YesSql map index on AIDocumentId, ReferenceId, ReferenceType, and Index for efficient chunk queries.
  • Simplified indexing pipeline — During re-indexing, stored embeddings from AIDocumentChunk.Embedding are used directly. The indexing code no longer resolves embedding generators from index profile metadata at indexing time. Embeddings are generated once during document upload/processing.
  • Document tool updatesReadDocumentTool and ReadTabularDataTool now reconstruct full text by querying chunks from the chunk store and concatenating in order, instead of reading a Text property.

Breaking: AIDocument.Chunks and AIDocument.Text properties have been removed. Code that previously accessed document.Chunks must now use IAIDocumentChunkStore.GetChunksByAIDocumentIdAsync(documentId). Code that accessed document.Text should concatenate chunks from the store.

Breaking: AIDocumentChunk (the indexing DTO) has been renamed to AIDocumentChunkContext. The name AIDocumentChunk now refers to the new CatalogItem subclass used for chunk storage.


Breaking Changes

Typed AI Deployments

AI deployments are now first-class typed entities. Each deployment has a Type property (Chat, Utility, Embedding, Image, SpeechToText) and an IsDefault flag.

Key changes:

  • Connections are pure connections — deployment name fields (ChatDeploymentName, EmbeddingDeploymentName, UtilityDeploymentName, ImagesDeploymentName) on AIProviderConnection are deprecated
  • Typed deployment resolutionIAIDeploymentManager resolves deployments by type with fallback chain (explicit → connection default → global default)
  • Global default deployments — new settings page under Settings > Artificial Intelligence > Default Deployments to configure global defaults for Utility, Embedding, and Image deployments
  • AI ProfilesDeploymentId renamed to ChatDeploymentId, new UtilityDeploymentId field added
  • Chat Interactions — same changes as AI Profiles
  • Deployment dropdowns — now show all deployments grouped by connection for easier selection
  • Automatic migration — existing connection deployment names are automatically migrated to typed AIDeployment records on startup
  • appsettings.json — both old format (deployment names on connection) and new format (Deployments array) are supported

Breaking changes:

  • AIProviderConnection.ChatDeploymentName → deprecated (use typed AIDeployment records instead)
  • AIProviderConnection.EmbeddingDeploymentName → deprecated
  • AIProviderConnection.UtilityDeploymentName → deprecated
  • AIProviderConnection.ImagesDeploymentName → deprecated
  • AIProvider.DefaultChatDeploymentName → deprecated
  • AIProvider.DefaultEmbeddingDeploymentName → deprecated
  • AIProvider.DefaultUtilityDeploymentName → deprecated
  • AIProvider.DefaultImagesDeploymentName → deprecated
  • AIProfile.DeploymentId → renamed to ChatDeploymentId (old JSON property still deserialized for backward compat)
  • ChatInteraction.DeploymentId → renamed to ChatDeploymentId
  • AICompletionContext.DeploymentId → renamed to ChatDeploymentId

For detailed migration instructions, see the Migrating to Typed AI Deployments guide.

Changed: Navigation Paths

Orchard Core v3 removed the Configuration tab. Update any documentation or code that references:

  • Configuration → Features → Use Tools → Features
  • Configuration → Settings → Use Settings directly

Changed: Package Version Scheme

Package versions now use the 2.0.0-preview-XXXX scheme instead of 2.0.0-beta-XXXX.

Changed: Renamed OpenAIChatApp Resource to AIChatApp

The frontend resource OpenAIChatApp (CSS and JavaScript) has been renamed to AIChatApp to reflect that the chat UI is provider-agnostic and not specific to OpenAI.

If you reference these resources in custom views or templates, update the resource names:

- <style asp-name="OpenAIChatApp" at="Head"></style>
+ <style asp-name="AIChatApp" at="Head"></style>

- <script asp-name="OpenAIChatApp" at="Foot"></script>
+ <script asp-name="AIChatApp" at="Foot"></script>

Bug Fixes

Document Tools Fail to Resolve Chat Session Documents

Affected features: CrestApps.OrchardCore.AI.Documents.ChatSessions

When documents were uploaded to an AI Chat Session (via the chat widget or session UI), the AI model could not access them because:

  1. The DocumentOrchestrationHandler did not add session documents to the orchestration context, so the system message never told the model about them.
  2. The document tools (list_documents, read_document, read_tabular_data, search_documents) only queried by profile ID with the Profile reference type, ignoring session-scoped documents.

What changed:

  • DocumentOrchestrationHandler.BuildingAsync — Now adds session documents (from AdditionalProperties["Session"]) to context.Context.Documents alongside profile-level documents, so the system message template lists all available documents.
  • ListDocumentsTool — Now queries both profile-level and session-level documents when the resource is an AIProfile, combining the results.
  • ReadDocumentTool — Now validates document ownership against both the profile ID and the session ID, so documents from either source can be read.
  • ReadTabularDataTool — Previously only supported ChatInteraction resources. Now supports AIProfile resources and validates against both profile and session reference IDs.
  • SearchDocumentsTool — Now searches across both profile and session vector indexes when both scopes have documents, combining and deduplicating results by score.
  • DocumentPreemptiveRagHandler — Searches across both profile and session scopes. Also removed overly restrictive HasSessionDocuments AND condition in CanHandleAsync that prevented preemptive RAG for ChatInteraction-attached documents.

Upload Failure Errors Not Reported in Chat UI

When a document upload failed (e.g., unsupported file type, processing error), the error was only logged to the browser console. Users had no visual indication that their upload had failed.

What changed:

  • The chat widget now displays failed uploads as red error badges in the document bar, showing the file name and error message.
  • Users can dismiss individual error badges by clicking the close button.
  • HTTP-level upload failures and network errors are also surfaced to the user.

Chat Feedback Metrics Use Majority Voting Instead of Per-Message Counts

Affected features: CrestApps.OrchardCore.AI.Chat, CrestApps.OrchardCore.AI.Agent

Each chat message can receive individual thumbs up or thumbs down feedback, but the session-level analytics aggregated all per-message ratings into a single boolean (UserRating) using majority voting. This caused inaccurate reporting—for example, a session with 3 thumbs up and 2 thumbs down would report a single "positive" rating instead of the actual counts.

What changed:

  • AIChatSessionEvent — Added ThumbsUpCount and ThumbsDownCount (int) properties to store per-session counts alongside the legacy UserRating field for backward compatibility.
  • AIChatSessionMetricsIndex — Added corresponding index columns with an UpdateFrom2Async migration.
  • AIChatHub.RateMessage — Now computes actual thumbs up/down counts from the per-message ratings and passes them to the event service, instead of using majority voting.
  • AIChatAnalyticsFeedbackDisplayDriver — Uses Sum() of count fields across sessions instead of counting sessions with positive/negative rating.
  • QueryChatSessionMetricsTool — Uses Sum() of count fields for accurate analytics reporting.
  • ChatAnalyticsFeedback.cshtml — Updated tooltip text to clarify these are message-level rating counts.

Post-Session Tasks Silently Fail With No Retry

Affected features: CrestApps.OrchardCore.AI

When a chat session closed, post-session tasks (data extraction, email sending) ran inline and if the AI service was unavailable, the error was caught, logged, and the task was permanently lost. There was no retry mechanism.

What changed:

  • AIChatSession — Added PostSessionProcessingStatus (enum: None, Pending, Completed, Failed), PostSessionProcessingAttempts (int), and PostSessionProcessingLastAttemptUtc (DateTime?) fields for tracking.
  • AIChatSessionIndex — Added PostSessionProcessingStatus column with an UpdateFrom2Async migration.
  • PostSessionProcessingChatSessionHandler — Now marks sessions as Pending before attempting processing, then Completed on success. On failure, status stays Pending so the background task can retry.
  • AIChatSessionCloseBackgroundTask — Rewritten with two-phase processing: Phase 1 closes inactive sessions, Phase 2 retries pending post-session tasks with a maximum of 3 attempts and a 5-minute retry delay. Sessions exceeding max attempts are marked Failed.

FunctionInvocationMetadata Not Read From Legacy Profile Property Key

Affected features: CrestApps.OrchardCore.AI

Profiles created in earlier versions stored tool selections under the JSON property key AIProfileFunctionInvocationMetadata. After the model was renamed to FunctionInvocationMetadata, the tool resolution code only checked the new key name, silently ignoring tool selections saved under the legacy key.

What changed:

  • AIProfileCompletionContextBuilderHandler — Now falls back to reading from the legacy AIProfileFunctionInvocationMetadata property key when the current FunctionInvocationMetadata key has no tool names.
  • AIProfileToolsDisplayDriver — The edit view reads from both keys for backward compatibility, correctly pre-selecting tools saved under the legacy key. On save, the legacy key is removed and data is written only under the current key, completing the migration.

Post-Close Processing Not Resilient for Analytics and Conversion Goals

Affected features: CrestApps.OrchardCore.AI

When a chat session closed, analytics recording (resolution detection and conversion goal evaluation) ran as a separate step outside the retry pipeline. If the AI service was unavailable at that moment, analytics were permanently lost with no retry. Additionally, post-session tasks that returned empty results were incorrectly left in Pending status instead of being marked as completed. Conversion goals were evaluated inside the analytics step, so a failure in either would prevent both from completing.

What changed:

  • AIChatSession — Added IsPostSessionTasksProcessed, IsAnalyticsRecorded, and IsConversionGoalsEvaluated boolean flags to independently track which processing steps have completed, enabling partial-completion-aware retries.
  • AIChatSessionCloseBackgroundTask — Refactored into three independent resilient steps: (1) post-session tasks, (2) session analytics recording, and (3) conversion goal evaluation. Each step is tracked and retried independently. The retry loop only re-runs steps that haven't completed yet. Overall status is marked Completed only when all applicable steps succeed. Comprehensive structured logging added at every step boundary (start, complete, skip, fail, retry) for production debugging.
  • PostSessionProcessingChatSessionHandler — Fixed a bug where successful processing that returned empty results left the session stuck in Pending status. Now correctly marks IsPostSessionTasksProcessed = true regardless of whether the AI produced output.
  • PostSessionProcessingService — Removed internal try/catch blocks from ProcessAsync, EvaluateResolutionAsync, and EvaluateConversionGoalsAsync. Previously, exceptions (e.g., AI model returning non-JSON responses) were caught internally and masked as empty results, preventing the retry pipeline from detecting failures. Exceptions now propagate to callers, allowing the background task's retry mechanism to work correctly.
  • PostSessionProcessingService.ProcessAsync — Fixed tool execution failing when post-session tasks require AI tools (e.g., sendEmail). The method used GetResponseAsync<T> (structured JSON output) which conflicts with tool calling — the model was forced to produce JSON instead of executing tool calls. When tools are configured, the method now uses non-generic GetResponseAsync to allow tool execution, then attempts to parse structured results from the response text. Additionally, the raw IChatClient from GetChatClientAsync lacked FunctionInvokingChatClient middleware, so tool_call messages returned by the model were never actually executed. The client is now wrapped with .AsBuilder().UseFunctionInvocation().Build() to enable tool execution.
  • PostSessionProcessingService — Prompt Template Refactoring — All hardcoded prompt-building methods (BuildProcessingPrompt, BuildConversationTranscript, and inline goal prompt construction) have been replaced with Liquid-based AI prompt templates. Three new user prompt templates were added: post-session-analysis-prompt, resolution-analysis-prompt, and conversion-goal-evaluation-prompt. These templates use Liquid {% for %} loops to dynamically render tasks, goals, and conversation transcripts, making prompts configurable and maintainable without code changes.

Post-Session Tasks Stuck in Pending Status After Processing

Affected features: CrestApps.OrchardCore.AI

When a post-session task (e.g., summary) was configured with tool capabilities (e.g., sendEmail), the AI model might not return structured JSON results even though the tool executed. In this case, ProcessAsync returned null and the task remained in Pending status indefinitely—it was never marked as Failed or Succeeded. Additionally, ProcessedAtUtc was never set for tasks that stayed pending, and the exception handler used DateTime.UtcNow instead of IClock.

What changed:

  • PostSessionResult — Added Attempts property (int) to track how many times each individual task has been processed. This enables per-task retry logic independent of the session-level retry counter.
  • PostSessionProcessingService.ProcessWithToolsAsync — Fixed the root cause where the method returned null when the AI model's response was not strict JSON, discarding valid results. The method now uses a multi-strategy response parser: (1) direct JSON deserialization, (2) JSON extraction from markdown code fences, (3) JSON object extraction from surrounding text. When all JSON parsing strategies fail and there is a single semantic task, the response text is used directly as the task value. Additionally, the response text is now extracted from the last assistant message with text content, skipping intermediate tool call and tool result messages (e.g., "Email sent successfully") that could be mistakenly picked up as the model's final output. Added comprehensive debug logging of the raw AI response text and each parsing strategy attempted.
  • PostSessionProcessingService.ApplyResults — Added per-task debug logging for applied results and skipped results to aid troubleshooting.
  • AIChatSessionCloseBackgroundTask.RunPostSessionTasksAsync — Before calling ProcessAsync, the method now increments Attempts for each non-succeeded task. After processing, tasks that produced no result and have exhausted all retry attempts (3) are permanently marked as Failed with ProcessedAtUtc set via IClock. Tasks below the attempt limit remain Pending for the next retry cycle. The completion check (IsPostSessionTasksProcessed) now considers tasks fully processed when all are either Succeeded or permanently Failed, allowing session-level processing to complete even when individual tasks fail.
  • AIChatSessionCloseBackgroundTask (catch block) — Fixed DateTime.UtcNow usage to use IClock.UtcNow. On exception, the error message is recorded on all non-succeeded tasks, but only tasks that have reached the maximum attempt count are marked as Failed. Tasks with remaining attempts stay Pending for retry.

Feedback Progress Bar Icon and Percentage Misaligned

Affected features: CrestApps.OrchardCore.AI.Chat

In the User Feedback analytics card, the thumbs icon appeared at the left edge of the progress bar segment while the percentage text was centered, making the layout look off-balance.

What changed:

  • ChatAnalyticsFeedback.cshtml — Progress bar segments now use flexbox (d-flex align-items-center justify-content-center gap-1) to center both the icon and percentage as a group, with horizontal padding for better spacing.

JSON.parse Error on AI Profile and Chat Interaction Edit Pages

Affected features: CrestApps.OrchardCore.AI.Prompting

When editing an AI Profile or Chat Interaction that uses prompt templates, the browser threw SyntaxError: JSON.parse: unexpected end of data at line 1 column 2 because Html.Raw() was used inside HTML data-* attributes. The JSON double-quote characters broke the attribute boundary, truncating the JSON to just {.

What changed:

  • AIProfilePromptSelection.Edit.cshtml — Removed Html.Raw() wrapper from data-json and data-params attributes. Razor's default HTML encoding produces &quot; which the browser's dataset API automatically decodes, so JSON.parse() receives valid JSON.
  • ChatInteractionPromptSelection.Edit.cshtml — Same fix applied.

SignalR Reconnection Loses Initial Chat Session

Affected features: CrestApps.OrchardCore.AI.Chat

When the SignalR WebSocket connection dropped during initial connection establishment (observed in production as a sub-second disconnect/reconnect cycle), the StartSession hub invocation was lost. The onreconnected handler only logged a message but did not retry session creation, leaving the chat widget with no session and no initial prompts displayed.

What changed:

  • ai-chat.js — The onreconnected handler now checks whether a session was already established. If the session was started, it reloads the current session to restore state. If no session was started and autoCreateSession is enabled, it retries startNewSession() to recover from the lost initial invocation.

AI Template Service Warning Logging

Affected features: CrestApps.OrchardCore.AI.Prompting

When an AI template ID was requested but not found (e.g., due to a typo or a disabled feature), the service returned null silently with no diagnostic output, making it difficult to troubleshoot missing prompt templates in production.

What changed:

  • OrchardCoreAITemplateService — Now logs a warning with the requested template ID and all available template IDs when a template lookup fails. This helps diagnose template resolution issues in production logs.

Profile-Attached Documents Not Indexed in Vector Search

Affected features: CrestApps.OrchardCore.AI.Documents.Profiles

Documents uploaded to an AI Profile's knowledge base (via the AI Profile editor in the admin dashboard) were saved to the YesSql document store but never pushed to the vector search index. This meant the preemptive RAG pipeline and search_documents tool could not find profile-attached documents, effectively making the knowledge base non-functional for profiles.

The same indexing logic already worked correctly for chat session documents (uploaded via the session endpoint), because UploadSessionDocumentEndpoint scheduled a deferred task to push document chunks to all AI document index profiles. The profile driver (AIProfileDocumentsDisplayDriver) was missing this step entirely.

What changed:

  • AIProfileDocumentsDisplayDriver.UpdateAsync — After processing and storing new documents, the driver now schedules a deferred task (ShellScope.AddDeferredTask) that pushes document chunks to all configured AI document vector search index profiles, following the exact same pattern used by UploadSessionDocumentEndpoint.IndexDocumentChunksAsync.
  • AIProfileDocumentsDisplayDriver.UpdateAsync — When existing documents are removed, the driver now also schedules a deferred task to remove the corresponding chunks from all vector search index profiles, matching the RemoveSessionDocumentEndpoint.RemoveDocumentChunksAsync pattern.
  • Added IndexDocumentChunksAsync and RemoveDocumentChunksAsync static methods to the driver for deferred task execution.

Enhanced Debug Logging for AI Document Processing and Orchestration

Affected features: CrestApps.OrchardCore.AI, CrestApps.OrchardCore.AI.Documents

Production debugging of document processing, RAG retrieval, and system message composition was difficult because there was insufficient logging at key decision points. Added comprehensive LogLevel.Debug logging throughout the pipeline to enable end-to-end traceability.

What changed:

  • DefaultOrchestrationContextBuilder — Logs the final composed system message length, content preview, and resource type after all orchestration handlers have run, immediately before handing off to the AI model.
  • DocumentOrchestrationHandler — Added ILogger injection. Logs the number of documents populated from chat interactions and AI profiles during the BuildingAsync phase, and logs when no documents are found.
  • DocumentPreemptiveRagHandler — Logs at every decision point: index profile lookup result, vector search service availability, embedding configuration completeness, resolved search scopes (with resource IDs and reference types), query count, topN setting, and search result count.
  • DefaultAIDocumentProcessingService — Logs text extraction result length, chunk count after normalization, embedding generation success, and reasons for skipping embedding generation (unsupported extension, empty text, no generator).
  • AIProfileDocumentsDisplayDriver — Logs when vector index push and chunk removal tasks are scheduled.

All debug log statements are guarded with _logger.IsEnabled(LogLevel.Debug) to avoid argument evaluation overhead when debug logging is disabled.

AI Templates Not Discovered in NuGet/Docker Deployments

Affected features: CrestApps.OrchardCore.AI.Prompting

AI prompt templates (.md files in AITemplates/Prompts/ directories) were not discovered at runtime when modules were deployed via NuGet packages (e.g., in Docker containers). The ModuleAITemplateProvider relied on IHostEnvironment.ContentRootFileProvider.GetDirectoryContents() to enumerate template files, which only finds physical files on disk. In NuGet deployments, templates are embedded as assembly resources by OrchardCore's Module Targets, but the ContentRootFileProvider-based discovery and the AppDomain.CurrentDomain.GetAssemblies() fallback were both unreliable in containerized environments.

What changed:

  • ModuleAITemplateProvider — Rewritten to use IApplicationContext.Application.GetModule(), following the same pattern as OrchardCore's ModuleEmbeddedFileProvider. The provider now enumerates each module's AssetPaths to find templates matching the AITemplates/Prompts/ convention, then reads file contents via Module.GetFileInfo() which resolves embedded resources directly from the assembly. This approach works reliably in both development (project references) and production (NuGet/Docker) environments without depending on IHostEnvironment.ContentRootFileProvider timing or AppDomain.CurrentDomain.GetAssemblies() assembly loading order.
  • EmbeddedResourceAITemplateProvider — Now handles both standard . separators and OrchardCore's > path separators in embedded resource logical names, making it compatible with assemblies built using OrchardCore.Module.Targets.

Initial Prompt Not Displayed on New Chat Sessions

Affected features: CrestApps.OrchardCore.AI.Chat

When an AI Profile had an InitialPrompt configured, new chat sessions displayed a blank white page instead of triggering the initial prompt. The view correctly set autoCreateSession: true and hid the placeholder, but the session was created with zero messages. Since the LoadSession payload contained no messages and the placeholder was hidden, users saw an empty chat area.

What changed:

  • ai-chat.js — The LoadSession handler now checks if the session is new (zero messages) and an initialPrompt is configured. When both conditions are met, it automatically sends the initial prompt as the first user message, triggering an AI response.
  • Widget-AIChat.cshtml, AIChatAdminWidget.cshtml, AIChatSessionChat.cshtml — All chat views now pass the initialPrompt text in the JavaScript configuration object (JSON-encoded via JsonSerializer.Serialize) so the client-side code can auto-send it.

SendEmail Tool Crashes When Invoked From Background Task

Affected features: CrestApps.OrchardCore.AI.Agent

When a chat session was closed by the AIChatSessionCloseBackgroundTask (inactivity timeout), post-session tasks that invoke the sendEmail tool failed silently. The SendEmailTool.InvokeCoreAsync method accessed httpContextAccessor.HttpContext.User without null-checking HttpContext, which is null in background task contexts (no HTTP request). This caused a NullReferenceException that was caught by FunctionInvokingChatClient, retried up to 3 times (all failing), and ultimately resulted in the AI model returning non-JSON text that could not be parsed — producing 0 post-session results with no visible error.

Sessions closed inline during an HTTP request (via PostSessionProcessingChatSessionHandler) were not affected because HttpContext is available in that path.

What changed:

  • SendEmailTool.InvokeCoreAsync — Changed httpContextAccessor.HttpContext.User to httpContextAccessor.HttpContext?.User with a null guard. When HttpContext is null (background task), the email is sent without a sender address (uses the system default), instead of crashing.
  • GetContentItemLinkTool.InvokeCoreAsync — Added null HttpContext guard. When invoked from a background task, returns a descriptive fallback message instead of crashing.
  • CreateOrUpdateContentTool.InvokeCoreAsync — Added null HttpContext guard. Skips URI generation when running in a background context and logs a debug warning.
  • PostSessionProcessingService — Added comprehensive LogLevel.Debug logging throughout the processing pipeline: tool resolution results (success/failure per tool), code path selection (tools vs structured output), AI response details (message count, tool call count, tool result count), and JSON parse failures with truncated response text. All debug statements are guarded with _logger.IsEnabled(LogLevel.Debug).

Per-Task Post-Session Result Tracking

Affected features: CrestApps.OrchardCore.AI, CrestApps.OrchardCore.AI.Chat

Previously, post-session processing was all-or-nothing: all tasks were sent to the AI model in a single request, and if any task (or the request itself) failed, all tasks were lost and retried together on the next attempt. There was no visibility into which individual tasks succeeded or failed.

What changed:

  • PostSessionTaskResultStatus — New enum (Pending, Succeeded, Failed) for tracking individual task outcomes.
  • PostSessionResult — Extended with Status (PostSessionTaskResultStatus) and ErrorMessage fields. Each result now records whether the task succeeded, failed, or is still pending.
  • PostSessionProcessingService.ProcessAsync — Now reads the session's existing PostSessionResults and filters out tasks that have already succeeded. Only tasks with Pending or Failed status are included in the AI prompt. On success, returned results are marked with Status = Succeeded.
  • AIChatSessionCloseBackgroundTask.RunPostSessionTasksAsync — Completely rewritten for per-task tracking. Initializes Pending entries for all configured tasks, merges results from AI processing, and marks tasks as Failed with error messages on exceptions. IsPostSessionTasksProcessed is only set to true when all tasks have Status = Succeeded.
  • PostSessionProcessingChatSessionHandler.MessageCompletedAsync — Rewritten with the same per-task tracking and merge logic as the background task, ensuring consistent behavior between inline and background processing paths.

This means: if a profile has 3 post-session tasks and task 2 fails, tasks 1 and 3 are preserved as Succeeded and only task 2 is retried on the next processing attempt. The PostSessionResults dictionary now provides full per-task visibility for debugging and monitoring.

AI Resolution Detection Decoupled From Session Metrics

Affected features: CrestApps.OrchardCore.AI, CrestApps.OrchardCore.AI.Chat

The Enable AI Resolution Detection checkbox was previously nested inside the session metrics settings section and only visible when Enable Session Metrics was checked. However, resolution detection enhances session closing logic (determining whether a user's query was resolved) and is useful independently of metrics collection.

What changed:

  • AIProfileAnalytics.Edit.cshtml — Moved the "Enable AI Resolution Detection" checkbox outside the session metrics wrapper so it is always visible regardless of metrics toggle state. Updated hint text to clarify independent operation.
  • AIChatSessionCloseBackgroundTask — Updated the needsAnalytics and analyticsComplete conditions to consider EnableAIResolutionDetection independently of EnableSessionMetrics. Resolution detection now runs even when session metrics are disabled.

Admin Widget "Profile Not Found" After Profile Deletion

Affected features: CrestApps.OrchardCore.AI.Chat

When the AI profile used by the admin chat widget was deleted or reconfigured, the widget continued using the stale session ID from localStorage. The ReceiveError handler logged the "Profile not found" error to the console but did not clear the stale session or attempt recovery, leaving the widget permanently broken until the user manually cleared browser storage.

What changed:

  • ai-chat.js — The ReceiveError handler now checks for profile/session-not-found errors and clears the stored session ID from localStorage when detected. A recovery guard (_attemptedSessionRecovery) prevents infinite reconnection loops. After clearing the stale session, the widget automatically starts a new session with the current profile.

Migration Guide from v1.x

Step 1: Update Package References

Update all CrestApps package references to 2.0.0-preview-0001 or later:

<PackageReference Include="CrestApps.OrchardCore.Cms.Core.Targets" Version="2.0.0-preview-0001" />

Step 2: Remove Prompt Routing Code

If you used AddPromptProcessingIntent, IPromptIntentDetector, or IPromptProcessingStrategy, remove these references. The orchestrator now handles all request processing automatically.

Step 3: Update Tool Registrations

If you registered custom AI tools, update to the new fluent API:

// Old (v1.x)
// services.AddSingleton<AIFunction, MyTool>();

// New (v2.0)
services.AddAITool<MyTool>(MyTool.TheName)
    .WithTitle("My Tool")
    .WithDescription("Description for the orchestrator")
    .Selectable();

Step 4: Enable New Features

New modules are not enabled by default. Enable them via Tools → Features in the admin dashboard as needed.


Enhancements

AI Chat Session Analytics

  • AI Resolution Detection: Sessions are now analyzed by AI to determine whether the user's query was semantically resolved, replacing the timeout-only abandonment detection that produced 100% false-positive abandonment rates. Enabled by default when session metrics are active. Configurable per-profile via the Enable AI Resolution Detection checkbox.
  • Conversion Metrics: New goal-based conversion rate system. Define custom goals per profile with configurable scoring ranges (default 0–10). After session close, AI evaluates the conversation against each goal and produces per-goal scores with reasoning. Aggregate conversion metrics are stored on the session event and available in the analytics dashboard and queryChatSessionMetrics tool.
  • Resolution & Conversion Dashboard Card: New analytics dashboard section displaying resolution rate, resolved/unresolved session counts, average conversion score, high/low performing session percentages, and an overall conversion progress bar.
  • Enriched Workflow Events: All chat session workflow events (AIChatSessionClosedEvent, AIChatSessionPostProcessedEvent, AIChatSessionFieldExtractedEvent) now include full Session and Profile objects in the input dictionary, not just IDs.
  • All Fields Extracted Event: New AIChatSessionAllFieldsExtractedEvent workflow event that triggers once when all configured data extraction fields have been collected for a session.
  • ExtractedFieldChange Liquid Access: ExtractedFieldChange and ConversionGoalResult types are now registered in the Liquid MemberAccessStrategy for use in workflow expressions.

AI Profile Storage

  • Per-Document Storage: AI Profiles are now stored as individual YesSql documents instead of a single DictionaryDocument<AIProfile>. This eliminates SQL Server nvarchar(max) size limits that could break with large numbers of profiles, making the storage layer more scalable and robust.
  • AIProfileIndex: A new AIProfileIndex MapIndex provides efficient querying by Name, Type, ConnectionName, DeploymentId, OrchestratorName, OwnerId, Author, and IsListable columns. This replaces the previous in-memory filtering approach.
  • Automatic Data Migration: Existing tenants with profiles stored in the legacy DictionaryDocument format are automatically migrated to individual documents on startup. No manual intervention is required.
  • DefaultAIProfileStore: Now extends NamedSourceDocumentCatalog<AIProfile, AIProfileIndex> (YesSql per-document pattern) instead of NamedCatalog<AIProfile> (single-document pattern). The store is registered for ICatalog<AIProfile>, ISourceCatalog<AIProfile>, INamedCatalog<AIProfile>, and INamedSourceCatalog<AIProfile>.

Post-Session Processing

  • Tool Capabilities: Post-session processing now supports AI tool invocation. A new Capabilities tab in the post-session configuration allows selecting AI tools to make available during analysis. The AI model can invoke these tools during post-session processing for actions beyond text generation.
  • Per-Task Result Tracking: Each post-session task is now tracked individually with Pending, Succeeded, or Failed status. Tasks that succeed are preserved across retries — only tasks that haven't succeeded are retried. The PostSessionResults dictionary provides full per-task visibility including error messages for failed tasks.
  • Removed Token Limit: The MaxOutputTokens = 2048 limit has been removed from post-session processing requests, allowing the AI model to use its default output limits for more thorough analysis.

AI Resolution Detection

  • Independent of Session Metrics: The "Enable AI Resolution Detection" setting is now always visible and operates independently of the "Enable Session Metrics" toggle. Resolution detection enhances session closing logic beyond just metrics collection.

Conversion Metrics

  • Independent of Session Metrics: The "Enable Conversion Metrics" setting and its associated goals configuration are now always visible and operate independently of the "Enable Session Metrics" toggle. Conversion goals can be defined and evaluated without enabling session metrics.

Typed AI Deployments

  • First-Class Typed Deployments: AIDeployment is now a typed entity with a Type property (Chat, Utility, Embedding, Image, SpeechToText) and an IsDefault boolean. This replaces the old pattern of storing deployment names directly on provider connections.
  • Pure Connections: AIProviderConnection no longer carries deployment name fields (ChatDeploymentName, UtilityDeploymentName, EmbeddingDeploymentName, ImagesDeploymentName). Connections are now pure connection configurations. These fields are deprecated and auto-migrated to the new Deployments array format.
  • Default AI Deployment Settings: A new settings page under Settings → Artificial Intelligence → Default AI Deployment Settings allows configuring global defaults: DefaultUtilityDeploymentId, DefaultEmbeddingDeploymentId, DefaultImageDeploymentId.
  • Deployment Resolution Fallback: The system resolves deployments using a fallback chain: explicit assignment → connection default for type → global default → null/error. When resolving a Utility deployment and no utility deployment is found at any level, the system automatically falls back to the Chat resolution chain as a last resort.
  • AI Profile & Chat Interaction Fields: ChatDeploymentId and UtilityDeploymentId replace the single DeploymentId field on AI Profiles and Chat Interactions.
  • Grouped Deployment UI: UI dropdowns now show deployments grouped by connection instead of the previous cascading connection → deployment selection.
  • Configuration Format: appsettings.json now uses a Deployments array on each connection. Provider-level DefaultChatDeploymentName, DefaultUtilityDeploymentName, DefaultEmbeddingDeploymentName, DefaultImagesDeploymentName are deprecated.

Agentic Framework

  • Agent Profile Type: A new Agent value has been added to AIProfileType. Agent profiles are reusable, composable agents that are automatically exposed as AI tools via the tool registry.
  • Agent-as-Tool: Each Agent profile is dynamically registered as an AI tool by AgentToolRegistryProvider. Other profiles can invoke agents during orchestration, and the AI model determines which agent to call based on the agent's description.
  • Agent Selection UI: AI Profiles, AI Templates, and Chat Interactions now include an Agents section under the Capabilities tab, displayed before Tools. Agents are shown as checkboxes, separate from the Tools section for clarity.
  • Agent Availability Modes: Agents support two availability modes via AgentMetadata.Availability:
    • On demand (default): Agents are only included in AI requests when matched by semantic or keyword relevance scoring. Users select on-demand agents from the Capabilities tab.
    • Always available: Agents are automatically included in every completion request. These agents do not appear in the Capabilities tab checkbox lists since they are always active. A warning is shown when selecting this mode to inform about increased token usage.
  • Description Field: AIProfile now has a Description property, required for Agent profiles. The description is used as the tool description when the agent is exposed as an AI tool.
  • AgentNames on Context: AICompletionContext, ChatInteraction, and ProfileTemplateMetadata now include AgentNames for selecting which agents to include.
  • Built-in Agent Templates: Eight agent templates are included out of the box: Planner Agent (structured task planning), Research Agent (information gathering and synthesis), Executor Agent (step-by-step plan execution), Writer Agent (content drafting and polishing), Reviewer Agent (critical review and feedback), Data Analyst Agent (data analysis and insights), Summarizer Agent (content condensation), and Code Assistant Agent (software development assistance).
  • Template Support: ModuleAIProfileTemplateProvider and AppDataAIProfileTemplateProvider now support AgentNames, ProfileDescription, and AgentAvailability in template front matter metadata.
  • AgentProxyTool: The agent proxy tool parameter has been renamed from task to prompt to align with standard agentic framework terminology.
  • Agent Context Injection: The new AgentOrchestrationContextBuilderHandler enriches the system message with descriptions of available agents. This follows the industry-standard pattern (OpenAI, LangChain, CrewAI) where capability descriptions are included in the system prompt so the model can make informed routing decisions. Agent descriptions are lightweight (~50 tokens per agent) and only included when agents are configured for the profile.
  • Task Planning with Agents: The task planning prompt template now includes Agent as a source type. When agents are available, the planner lists them first (before other tools) with instructions to prefer delegating to agents for complex subtasks.
  • Capabilities Tab Ordering: The Capabilities tab now orders sections as: MCP Connections → Agents → Tools (previously MCP → Tools → Agents).

Chat Mode (Unified Voice Settings)

  • Chat Mode Dropdown: AI Chat profiles, AI Profile Templates, and Chat Interactions now use a unified Chat Mode dropdown instead of separate speech-to-text and text-to-speech checkboxes. The dropdown offers three options: Text Only (default), Audio Input, and Conversation. For AI Profiles and Templates, the dropdown only appears for Chat profile types.
  • Audio Input Mode: Adds a microphone button (🎤) to the chat UI. Users can speak their prompts instead of typing. Audio is streamed in chunks (~1 second intervals) via SignalR to the server, where it is transcribed in near real-time using ISpeechToTextClient. Transcript text is sent back to the UI as the user speaks — words appear progressively, providing immediate feedback. Errors (e.g., authentication failures) are reported immediately and recording stops automatically.
  • Conversation Mode: Enables persistent two-way voice interaction like ChatGPT voice mode. A headset button appears in the chat UI. Clicking it opens a continuous audio stream — the mic, send button, and text input are hidden. Speak naturally → speech is transcribed and displayed as a user message → the prompt is automatically sent to the AI → the AI response streams as both text and spoken audio simultaneously. If you speak while the AI is still responding, the current response is interrupted to process your new prompt. Click the headset button again to close the stream and restore the text interface.
  • Profile/Template Setting: The Chat Mode dropdown on AI Profiles and AI Profile Templates (Profile source) controls the voice interaction mode. The dropdown is only visible for Chat profile types and when the required default deployments are configured. When Conversation is selected, a Voice dropdown appears (populated via AJAX from the configured TTS deployment). A "Default voice" option is always available.
  • Chat Interaction Setting: A site-level Chat Mode dropdown under Settings → AI → Chat Interactions configures the voice interaction mode for all Chat Interaction UIs. No per-session voice selection — uses the default voice from site settings.
  • Deployment Requirements: Audio Input requires a valid DefaultSpeechToTextDeploymentId in the Default AI Deployment Settings. Conversation requires both DefaultSpeechToTextDeploymentId and DefaultTextToSpeechDeploymentId.
  • GStreamer Requirement: The Azure Speech SDK requires GStreamer on all platforms (Windows, Linux, macOS) to decode compressed audio formats (OGG/Opus, WebM/Opus). See Azure OpenAI — GStreamer Requirement for installation instructions.
  • Audio Format Passthrough: The browser's recording MIME type (e.g., audio/webm;codecs=opus) is passed through the SignalR hub to the speech-to-text provider, which maps it to the appropriate SDK audio format. Unsupported formats fall back to auto-detection.
  • Settings Models: ChatModeProfileSettings (with ChatMode enum and VoiceName string) replaces the previous SpeechToTextProfileSettings and TextToSpeechProfileSettings. ChatInteractionChatModeSettings (with ChatMode enum) replaces the previous ChatInteractionSpeechToTextSettings and ChatInteractionTextToSpeechSettings.

Contained-Connection Deployments

  • New Deployment Source: AI deployments can now embed their own connection parameters (endpoint, authentication, credentials) directly in the deployment's Properties, instead of referencing a shared AIProviderConnection. This supports standalone AI services like Azure Speech Service that have their own endpoint per model.
  • SupportsContainedConnection Flag: AIDeploymentProviderEntry gained a SupportsContainedConnection property. When true, the deployment creation UI hides the connection dropdown and relies on provider-specific display drivers for inline connection fields.
  • CreateSpeechToTextClientAsync(AIDeployment) Overload: IAIClientFactory gained a deployment-aware overload that auto-detects whether the deployment uses a connection reference or contained connection, then constructs the appropriate AIProviderConnectionEntry from the deployment's Properties.
  • Azure Speech Provider: A new AzureSpeech deployment provider is registered in the Azure OpenAI module. It supports inline Endpoint, Authentication Type (Default/ManagedIdentity/ApiKey), API Key, and Identity ID fields. The AzureSpeechClientProvider creates ISpeechToTextClient instances using the Azure Speech SDK for continuous real-time recognition. The SDK handles audio format detection, WebSocket connections, and supports multiple authentication methods.

Text-to-Speech Technical Details

  • Custom ITextToSpeechClient Interface: A new ITextToSpeechClient interface provides GetAudioAsync, GetStreamingAudioAsync, and GetVoicesAsync methods. This is a custom interface because Microsoft.Extensions.AI does not include a text-to-speech abstraction.
  • Azure Speech SDK Implementation: AzureSpeechServiceTextToSpeechClient uses the Azure Speech SDK's SpeechSynthesizer with StartSpeakingTextAsync() for streaming synthesis. Audio chunks are streamed via the Synthesizing event using a Channel-based pattern.
  • Voice Selection: Providers that support voice listing (e.g., Azure Speech) expose available voices via GetSpeechVoicesAsync. The AI Profile and Template editors show a voice dropdown when Conversation mode is selected. The default voice dropdown in site settings is an AJAX-populated select that fetches voices from the selected TTS deployment.
  • Hub Streaming: Both AIChatHub and ChatInteractionHub expose SynthesizeSpeech for on-demand TTS, and StartConversation for persistent two-way conversation mode. The StartConversation method receives a continuous audio stream, runs STT → AI completion → TTS in a loop, handles interruption (new speech cancels current AI response), and streams assistant tokens and audio back to the client simultaneously via ReceiveConversationAssistantToken, ReceiveConversationAssistantComplete, ReceiveAudioChunk, and ReceiveAudioComplete.
  • Frontend Playback: The client collects base64-encoded audio chunks, combines them into a single audio blob on completion, and plays using the HTML5 Audio API. A stop button allows cancelling playback at any time.
  • CreateTextToSpeechClientAsync Factory Methods: IAIClientFactory gained two overloads — one accepting a deployment name and one accepting an AIDeployment directly — following the same contained-connection pattern established by the speech-to-text factory methods.
  • Default Deployment Setting: A new DefaultTextToSpeechDeploymentId field in Settings → AI → Default Deployments specifies the global text-to-speech deployment.
  • Default Voice Setting: A new DefaultTextToSpeechVoiceId field in Settings → AI → Default Deployments specifies the default voice used when no profile-specific voice is configured. The voice dropdown is AJAX-populated based on the selected TTS deployment.
  • Voice Fallback Chain: The voice used for synthesis follows a fallback chain: client-specified voice → profile-specific voice → default voice from site settings → provider default.
  • Voice Language Grouping: SpeechVoice now includes a Language property. Voice dropdowns in the AI Profile editor and default deployment settings group voices by language with <optgroup> elements and sort alphabetically within each group.
  • Conversation Availability: Conversation mode is available across all chat UIs — the admin /ai-chat/session/{profileId} page, the frontend Widget-AIChat content widget, the AIChatAdminWidget copilot-style widget, and the Chat Interactions editor.
  • Graceful Stop: Stopping conversation mode closes the audio stream, stops audio playback, and restores the text input, send button, and microphone. The server finishes processing any in-flight transcription before closing.