Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions CS/ReportingApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
using System;
using System.IO;
using Azure;
using DevExpress.AIIntegration;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;

var builder = WebApplication.CreateBuilder(args);
Expand All @@ -43,13 +41,9 @@
new Uri(EnvSettings.AzureOpenAIEndpoint),
new AzureKeyCredential(EnvSettings.AzureOpenAIKey));

var chatClient = azureOpenAIClient.GetChatClient(EnvSettings.DeploymentName).AsIChatClient();

builder.Services.AddSingleton(chatClient);
builder.Services.AddSingleton<AgentFactory>(sp =>
new(azureOpenAIClient, EnvSettings.DeploymentName, sp.GetRequiredService<ILogger<AgentFactory>>()));
builder.Services.AddSingleton<IAIReportingChatService, AIReportingChatService>();
builder.Services.AddDevExpressAI(config => { });

var app = builder.Build();
using(var scope = app.Services.CreateScope()) {
Expand Down
2 changes: 0 additions & 2 deletions CS/ReportingApp/ReportingApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,13 @@
</Target>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageReference Include="DevExpress.AIIntegration.OpenAI" Version="26.1.2-beta" />
<PackageReference Include="DevExpress.AIIntegration.Agents" Version="26.1.2-beta" />
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.5.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.11" />
<PackageReference Include="Microsoft.Extensions.AI" Version="10.6.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.6.0" />
<PackageReference Include="System.Data.SQLite" Version="2.0.3" />
<PackageReference Include="DevExpress.AspNetCore.Reporting" Version="26.1.2-beta" />
<PackageReference Include="DevExpress.AIIntegration.Web" Version="26.1.2-beta" />
<PackageReference Include="DevExpress.Drawing.Skia" Version="26.1.2-beta" />
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
Expand Down
22 changes: 10 additions & 12 deletions CS/ReportingApp/Services/AIReportingChatService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@

namespace ReportingApp.Services {
public class AIReportingChatService : IAIReportingChatService, IAsyncDisposable {
const string SESSION_NOT_FOUND_ERROR = "Chat session not found";
const string DOCUMENTATION_FILE_NAME = "documentation.pdf";
const string DOCUMENT_ASSISTANT_PROMPT = "You are a data analysis assistant. Your task is to read information from PDF files and provide users with accurate data-driven answers based on the contents of these files. \n Key Responsibilities: \n - Perform data analysis, including data summaries, calculations, filtering, and trend identification.\n - Clearly explain your analysis process to ensure users understand how you reached your conclusions.\n - Provide precise and accurate responses strictly based on data in the file.\n - If the requested information is not available in the provided file's content, state: \"The requested information cannot be found in the data provided.\"\n - Avoid giving responses when data is insufficient for a reliable answer.\n - Ask clarifying questions when a user’s query is unclear or lacks detail.\n - Your primary goal is to deliver helpful insights that directly address user questions. Do not make assumptions or infer details not supported by data. Respond in plain text only, without sources, footnotes, or annotations.\n Avoid giving information about provided file name, assistants' IDs and other internal data";
const string USER_ASSISTANT_PROMPT = "You are a user interface assistant (you help people use a software program). Your role is to read information from documentation files in PDF format. You assist users by providing accurate answers to their questions based on information from these files. \r\n\r\nTasks:\r\nExtract relevant information from PDF documentation to answer user questions.\r\nClearly explain your reasoning process and give step by step solutions to ensure users understand how you arrived at your answers.\r\nAlways provide precise and accurate information based on content from the documentation file.\r\nIf you cannot find an answer based on provided documentation, explicitly state: 'The requested information cannot be found in documentation provided.'\r\n Respond in plain text only, without markdown, sources, footnotes, or annotations.";
const string SessionNotFoundError = "Chat session not found";
const string DocumentationFileName = "documentation.pdf";

readonly AgentFactory agentFactory;
readonly IWebHostEnvironment environment;

// Each chat session holds an IChatResponseProvider and a cleanup delegate that
// removes the uploaded OpenAI resources (file + vector store) when the session ends.
// removes the uploaded OpenAI resources (file and vector store) when the session ends.
ConcurrentDictionary<string, (IChatResponseProvider Provider, Func<Task> Cleanup)> sessions = new();

public AIReportingChatService(AgentFactory agentFactory, IWebHostEnvironment environment) {
Expand All @@ -30,35 +28,35 @@ async Task<string> RegisterSession(IChatResponseProvider provider, Func<Task> cl
return sessionId;
}

// Opens a Data Analysis chat for Web Document Viewer.
// Open a Data Analysis chat for the Web Document Viewer.
// The agent analyzes the exported report PDF and answers data-driven questions.
public async Task<string> OpenDocumentChatAsync(Stream data) {
var (provider, cleanup) = await agentFactory.CreateAgentWithFileAsync(
data, Guid.NewGuid().ToString() + ".pdf", DOCUMENT_ASSISTANT_PROMPT);
data, Guid.NewGuid().ToString() + ".pdf", AgentInstructions.DocumentAssistantPrompt);
return await RegisterSession(provider, cleanup);
}

// Opens a UI help chat for Web Report Designer.
// Open a UI help chat for the Web Report Designer.
// The agent reads the documentation PDF and answers questions about using the Designer.
public async Task<string> OpenDesignerChatAsync() {
string filePath = Path.Combine(environment.ContentRootPath, "Data", DOCUMENTATION_FILE_NAME);
string filePath = Path.Combine(environment.ContentRootPath, "Data", DocumentationFileName);
using var stream = File.OpenRead(filePath);
var (provider, cleanup) = await agentFactory.CreateAgentWithFileAsync(
stream, DOCUMENTATION_FILE_NAME, USER_ASSISTANT_PROMPT);
stream, DocumentationFileName, AgentInstructions.DesignerAssistantPrompt);
return await RegisterSession(provider, cleanup);
}

public IChatResponseProvider GetChatProvider(string sessionId) {
if(!string.IsNullOrEmpty(sessionId) && sessions.TryGetValue(sessionId, out var tuple))
return tuple.Provider;
throw new Exception(SESSION_NOT_FOUND_ERROR);
throw new Exception(SessionNotFoundError);
}

public async Task CloseChatAsync(string sessionId) {
if(sessions.TryRemove(sessionId, out var tuple))
await tuple.Cleanup();
else
throw new Exception(SESSION_NOT_FOUND_ERROR);
throw new Exception(SessionNotFoundError);
}

public async ValueTask DisposeAsync() {
Expand Down
8 changes: 5 additions & 3 deletions CS/ReportingApp/Services/AgentFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using OpenAI.VectorStores;

namespace ReportingApp.Services {
// The OpenAI.Responses API is for evaluation purposes only and is subject to change or removal in a future update.
// The following code suppresses the OPENAI001 diagnostic.
#pragma warning disable OPENAI001
public class AgentFactory {
readonly AzureOpenAIClient openAIClient;
Expand All @@ -25,9 +27,9 @@ public AgentFactory(AzureOpenAIClient openAIClient, string deployment, ILogger<A
this.logger = logger;
}

// Uploads a PDF stream to OpenAI, creates a short-lived vector store, and returns
// an IChatResponseProvider backed by a Responses API agent with file search + code
// interpreter tools. The cleanup delegate removes the uploaded resources.
// Upload a PDF stream to OpenAI, create a short-lived vector store, and return
// an IChatResponseProvider backed by a Responses API agent with File Search and
// Code Interpreter tools. The cleanup delegate removes the uploaded resources.
public async Task<(IChatResponseProvider Provider, Func<Task> Cleanup)> CreateAgentWithFileAsync(
Stream data, string fileName, string instructions, CancellationToken ct = default) {

Expand Down
29 changes: 29 additions & 0 deletions CS/ReportingApp/Services/AgentInstructions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace ReportingApp.Services {
public static class AgentInstructions {
// Instructions for the Web Document Viewer chat that analyzes an exported report PDF.
public static string DocumentAssistantPrompt = """
You are a data analysis assistant. Your task is to read information from PDF files and provide users with accurate data-driven answers based on the contents of these files.
Key Responsibilities:
- Perform data analysis, including data summaries, calculations, filtering, and trend identification.
- Clearly explain your analysis process to ensure users understand how you reached your conclusions.
- Provide precise and accurate responses strictly based on data in the file.
- If the requested information is not available in the provided file's content, state: "The requested information cannot be found in the data provided."
- Avoid giving responses when data is insufficient for a reliable answer.
- Ask clarifying questions when a user’s query is unclear or lacks detail.
- Your primary goal is to deliver helpful insights that directly address user questions. Do not make assumptions or infer details not supported by data. Respond in plain text only, without sources, footnotes, or annotations.
Avoid giving information about provided file name, assistants' IDs and other internal data.
""";

// Instructions for the Web Report Designer chat that answers UI questions from the documentation PDF.
public static string DesignerAssistantPrompt = """
You are a user interface assistant (you help people use a software program). Your role is to read information from documentation files in PDF format. You assist users by providing accurate answers to their questions based on information from these files.

Tasks:
Extract relevant information from PDF documentation to answer user questions.
Clearly explain your reasoning process and give step by step solutions to ensure users understand how you arrived at your answers.
Always provide precise and accurate information based on content from the documentation file.
If you cannot find an answer based on provided documentation, explicitly state: 'The requested information cannot be found in documentation provided.'
Respond in plain text only, without markdown, sources, footnotes, or annotations.
""";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public CustomReportStorageWebExtension(ReportDbContext dbContext) {
public override bool CanSetData(string url) {
// Determines whether a report with the specified URL can be saved.
// Add custom logic that returns **false** for reports that should be read-only.
// Return **true** if no valdation is required.
// Return **true** if no validation is required.
// This method is called only for valid URLs (if the **IsValidUrl** method returns **true**).

return true;
Expand Down
6 changes: 3 additions & 3 deletions CS/ReportingApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"license": "MIT",
"private": true,
"dependencies": {
"@devexpress/analytics-core": "25.2.2-beta",
"@devexpress/analytics-core": "26.1.2-beta",
"bootstrap": "^4.3.1",
"devexpress-reporting": "25.2.2-beta",
"devextreme-dist": "25.2.2-beta",
"devexpress-reporting": "26.1.2-beta",
"devextreme-dist": "26.1.2-beta",
"marked": "^14.1.3"
}
}
Loading
Loading