This example is an ASP.NET Core application with integrated DevExpress Reports and an AI assistant. User requests and assistant responses are displayed on-screen using the DevExtreme dxChat
component.
The AI assistant's role depends on the associated DevExpress Reports component:
- Data Analysis Assistant: An assistant for the DevExpress Web Document Viewer. This assistant analyzes report content and answers questions related to information within the report.
- UI Assistant: An assistant for the DevExpress Web Report Designer. This assistant explains how to use the Designer UI to accomplish various tasks. Responses are based on information from end-user documentation for DevExpress Web Reporting components.
Please note that AI Assistant initialization takes time. The assistant tab appears once Microsoft Azure scans the source document on the server side.
Note
We use the following versions of the Microsoft.Extensions.AI.*
libraries in our source code:
- Microsoft.Extensions.AI.Abstractions: 9.5.0
- Microsoft.Extensions.AI: 9.5.0
- Microsoft.Extensions.AI.OpenAI: 9.5.0-preview.1.25265.7
We do not guarantee compatibility or correct operation with higher versions.
Note
DevExpress AI-powered extensions follow the "bring your own key" principle. DevExpress does not offer a REST API and does not ship any built-in LLMs/SLMs. You need an active Azure/Open AI subscription to obtain the REST API endpoint, key, and model deployment name. These variables must be specified at application startup to register AI clients and enable DevExpress AI-powered Extensions in your application.
You need to create an Azure OpenAI resource in the Azure portal to use AI Assistants for DevExpress Reporting. Refer to the following help topic for details: Microsoft - Create and deploy an Azure OpenAI Service resource.
Once you obtain a private endpoint and an API key, register them as AZURE_OPENAI_ENDPOINT
and AZURE_OPENAI_APIKEY
environment variables. The EnvSettings.cs reads these settings. DeploymentName
in this file is a name of your Azure model, for example, GPT4o
:
public static class EnvSettings {
public static string AzureOpenAIEndpoint { get { return Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT"); } }
public static string AzureOpenAIKey { get { return Environment.GetEnvironmentVariable("AZURE_OPENAI_APIKEY"); } }
public static string DeploymentName { get { return "GPT4o"; } }
}
Files to Review:
Note
The availability of Azure Open AI Assistants depends on region. For additional guidance in this regard, refer to the following document: Azure OpenAI Service models -- Assistants (Preview).
Register AI services in your application. Add the following code to the Program.cs file:
using DevExpress.AIIntegration;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
using System;
// ...
var azureOpenAIClient = new AzureOpenAIClient(
new Uri(EnvSettings.AzureOpenAIEndpoint),
new AzureKeyCredential(EnvSettings.AzureOpenAIKey));
var chatClient = azureOpenAIClient.GetChatClient(EnvSettings.DeploymentName).AsIChatClient;
builder.Services.AddDevExpressAI(config =>
{
config.RegisterOpenAIAssistants(azureOpenAIClient, EnvSettings.DeploymentName);
});
Files to Review:
On the server side, the AIAssistantProvider
service manages assistants.
public interface IAIAssistantProvider {
IAIAssistant GetAssistant(string assistantName);
Task<string> CreateDocumentAssistant(Stream data);
Task<string> CreateUserAssistant();
void DisposeAssistant(string assistantName);
}
The AIAssistantCreator.CreateAssistantAsync
method uploads a file to OpenAI, configures tool resources, creates an assistant with specified instructions and tools, initializes a new thread, and returns the assistant and thread IDs. The generated assistant and thread IDs are then passed to the IAIAssistantFactory.GetAssistant
method, which returns an IAIAssistant
instance. The created instance is added to the application's assistant collection and is referenced by its unique name.
For information on OpenAI Assistants, refer to the following documents:
- OpenAI Assistants API overview
- Azure OpenAI: OpenAI Assistants client library for .NET
- OpenAI .NET API library
Files to Review:
The following image displays Web Document Viewer UI implemented in this example. The AI Assistant tab uses a dxChat
component to display requests and responses:
On the BeforeRender
event, add a new tab (a container for the assistant interface):
@model DevExpress.XtraReports.Web.WebDocumentViewer.WebDocumentViewerModel
@await Html.PartialAsync("_AILayout")
<script>
let aiTab;
function BeforeRender(sender, args) {
const previewModel = args;
const reportPreview = previewModel.reportPreview;
aiTab = createAssistantTab();
const model = aiTab.model;
previewModel.tabPanel.tabs.push(aiTab);
// ...
}
</script>
@{
var viewerRender = Html.DevExpress().WebDocumentViewer("DocumentViewer")
.Height("100%")
.ClientSideEvents(configure => {
configure.BeforeRender("BeforeRender");
})
.Bind(Model);
@viewerRender.RenderHtml()
}
@* ... *@
Once the document is ready, the DocumentReady
event handler sends a request to the server and obtains the assistant name:
async function DocumentReady(sender, args) {
const response = await sender.PerformCustomDocumentOperation(null, true);
if (response.customData && aiTab?.model) {
aiTab.model.chatId = response.customData;
aiTab.visible = true;
}
}
The PerformCustomDocumentOperation
method exports the report to PDF and creates an assistant based on the exported document:
// ...
public override async Task<DocumentOperationResponse> PerformOperationAsync(DocumentOperationRequest request, PrintingSystemBase printingSystem, PrintingSystemBase printingSystemWithEditingFields) {
using(var stream = new MemoryStream()) {
printingSystem.ExportToPdf(stream, printingSystem.ExportOptions.Pdf);
var assistantName = await AIAssistantProvider.CreateDocumentAssistant(stream);
return new DocumentOperationResponse {
DocumentId = request.DocumentId,
CustomData = assistantName,
Succeeded = true
};
}
}
See the following files for implementation details:
Each time a user sends a message, the onMessageEntered
event handler passes the request to the assistant:
//...
async function getAIResponse(text, id) {
const formData = new FormData();
formData.append('text', text);
formData.append('chatId', id);
lastUserQuery = text;
const response = await fetch(`/AI/GetAnswer`, {
method: 'POST',
body: formData
});
return await response.text();
}
// ...
function RenderAssistantMessage(instance, message) {
instance.option({ typingUsers: [] });
instance.renderMessage({ timestamp: new Date(), text: message, author: assistant.name, id: assistant.id });
}
// ...
onMessageEntered: async (e) => {
const instance = e.component;
instance.renderMessage(e.message);
instance.option({ typingUsers: [assistant] });
const userInput = e.message.text;
var response = await getAIResponse(userInput, assistant.id);
RenderAssistantMessage(instance, response);
}
// ...
AIController.GetAnswer
receives answers from the assistant.
The following image displays Web Report Designer UI implemented in this example. The AI Assistant tab uses a dxChat
component to display requests and responses:
On the BeforeRender
event, add a new tab (a container for the assistant interface):
@model DevExpress.XtraReports.Web.ReportDesigner.ReportDesignerModel
<script>
async function BeforeRender(sender, args) {
const tab = createAssistantTab(chatId);
args.tabPanel.tabs.push(tab);
}
</script>
@await Html.PartialAsync("_AILayout")
@{
var designerRender = Html.DevExpress().ReportDesigner("reportDesigner")
.Height("100%")
.ClientSideEvents(configure => {
configure.BeforeRender("BeforeRender");
})
.Bind(Model);
@designerRender.RenderHtml()
}
@section Scripts {
@* ... *@
<script src="~/js/aiIntegration.js"></script>
@designerRender.RenderScripts()
}
@* ... *@
On the BeforeRender
event, send a request to AIController
to create the assistant:
async function BeforeRender(sender, args) {
const result = await fetch(`/AI/CreateUserAssistant`);
}
The AIAssistantProvider.CreateUserAssistant
method creates an assistant using the documentation.pdf file (end-user documentation for Web Reporting Controls in the PDF format) and the specified prompt. See the AIAssistantProvider.cs file for implementation details.
Each time a user sends a message, the onMessageEntered
event handler passes the request to the assistant:
//...
async function getAIResponse(text, id) {
const formData = new FormData();
formData.append('text', text);
formData.append('chatId', id);
lastUserQuery = text;
const response = await fetch(`/AI/GetAnswer`, {
method: 'POST',
body: formData
});
return await response.text();
}
// ...
function RenderAssistantMessage(instance, message) {
instance.option({ typingUsers: [] });
instance.renderMessage({ timestamp: new Date(), text: message, author: assistant.name, id: assistant.id });
}
// ...
onMessageEntered: async (e) => {
const instance = e.component;
instance.renderMessage(e.message);
instance.option({ typingUsers: [assistant] });
const userInput = e.message.text;
var response = await getAIResponse(userInput, assistant.id);
RenderAssistantMessage(instance, response);
}
// ...
AIController.GetAnswer
receives answers from the specified assistant.
- DevExtreme Chat - Getting Started
- Reporting for ASP.NET Core - Summarize and Translate DevExpress Reports Using Azure OpenAI
- Reporting for Blazor - Integrate AI-powered Summarize and Translate Features based on Azure OpenAI
- AI Chat for Blazor - How to add DxAIChat component in Blazor, MAUI, WPF, and WinForms applications
- Rich Text Editor and HTML Editor for Blazor - How to integrate AI-powered extensions
(you will be redirected to DevExpress.com to submit your response)