Skip to content

Commit 5f5bb7b

Browse files
Merge pull request #108 from telerik/add-chat-ai-integration-example
update grid ai smart example to use the telerik extension telerik/ken…
2 parents 5bf7d30 + ab41f48 commit 5f5bb7b

File tree

4 files changed

+84
-134
lines changed

4 files changed

+84
-134
lines changed

Telerik.Examples.Mvc/Telerik.Examples.Mvc/Controllers/Grid/SmartGridController.cs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,32 @@
22
using Kendo.Mvc.UI;
33
using Microsoft.AspNetCore.Http;
44
using Microsoft.AspNetCore.Mvc;
5+
using Microsoft.Extensions.AI;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Newtonsoft.Json;
8+
using Newtonsoft.Json.Serialization;
9+
using OpenAI.Chat;
510
using System;
611
using System.Collections.Generic;
712
using System.ComponentModel;
813
using System.Data;
14+
using System.Linq;
15+
using System.Text.Json;
916
using System.Threading.Tasks;
17+
using Telerik.AI.SmartComponents.Extensions;
1018
using Telerik.Examples.Mvc.Models;
19+
using Telerik.SvgIcons;
1120

1221
namespace Telerik.Examples.Mvc.Controllers.Grid
1322
{
1423
public class SmartGridController : Controller
1524
{
1625
private readonly AiService _smartGridService;
26+
IChatClient _chatClient;
1727

18-
public SmartGridController(AiService smartGridService)
28+
public SmartGridController(IChatClient smartGridService)
1929
{
20-
_smartGridService = smartGridService;
30+
_chatClient = smartGridService;
2131
}
2232

2333
public IActionResult SmartGrid()
@@ -32,10 +42,37 @@ public IActionResult GetSales([DataSourceRequest] DataSourceRequest request)
3242
}
3343

3444
[HttpPost]
35-
public async Task<IActionResult> Analyze([FromBody] GridAnalysisRequest request)
45+
public async Task<IActionResult> Analyze([FromBody] GridAIRequest request)
3646
{
37-
var result = await _smartGridService.AnalyzeGridDataAsync(request.Instructions, request.GridJson);
38-
return Json(new { result });
47+
var messages = request.Contents.Select(dto => dto).ToList();
48+
49+
var options = new ChatOptions();
50+
options.AddGridChatTools(request.Columns);
51+
52+
List<Microsoft.Extensions.AI.ChatMessage> conversationMessages = request.Contents
53+
.Select(m => new Microsoft.Extensions.AI.ChatMessage(ChatRole.User, m.Text))
54+
.ToList();
55+
56+
if (_chatClient == null)
57+
{
58+
return StatusCode(500, "Chat service is not available.");
59+
}
60+
61+
ChatResponse completion = await _chatClient.GetResponseAsync(conversationMessages, options);
62+
63+
GridAIResponse response = completion.ExtractGridResponse();
64+
65+
return new ContentResult
66+
{
67+
Content = System.Text.Json.JsonSerializer.Serialize(
68+
response,
69+
new System.Text.Json.JsonSerializerOptions
70+
{
71+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
72+
}),
73+
ContentType = "application/json"
74+
};
75+
3976
}
4077

4178
private List<SaleRecord> GetFullSalesData()

Telerik.Examples.Mvc/Telerik.Examples.Mvc/Program.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using AutoMapper;
22
using AutoMapper.Internal;
3+
using Azure;
4+
using Azure.AI.OpenAI;
35
using Kendo.Mvc.Extensions;
46
using Kendo.Mvc.UI;
57
using Microsoft.AspNetCore.Builder;
@@ -13,6 +15,7 @@
1315
using Microsoft.Extensions.Configuration;
1416
using Microsoft.Extensions.DependencyInjection;
1517
using Microsoft.Extensions.Hosting;
18+
using Microsoft.Extensions.AI;
1619
using Microsoft.OData.Edm;
1720
using Microsoft.OData.ModelBuilder;
1821
using Newtonsoft.Json.Serialization;
@@ -23,6 +26,7 @@
2326
using System.Threading;
2427
using System.Threading.Tasks;
2528
using Telerik.Examples.Mvc.Controllers;
29+
using Telerik.AI.SmartComponents.Extensions;
2630
using Telerik.Examples.Mvc.Database;
2731
using Telerik.Examples.Mvc.Hubs;
2832
using Telerik.Examples.Mvc.Models;
@@ -57,6 +61,18 @@
5761
builder.Services.AddTransient<CarsService>();
5862
builder.Services.AddTransient<AiService>();
5963

64+
// Register the Azure OpenAI client.
65+
builder.Services.AddSingleton(new AzureOpenAIClient(
66+
new Uri(builder.Configuration["OpenAI:Endpoint"]),
67+
new AzureKeyCredential(builder.Configuration["OpenAI:ApiKey"])
68+
));
69+
70+
// Register the Chat client with the specified model.
71+
builder.Services.AddChatClient(services =>
72+
services.GetRequiredService<AzureOpenAIClient>()
73+
.GetChatClient("gpt-4.1-nano").AsIChatClient()
74+
);
75+
6076
builder.Services.AddMvc()
6177
.AddNewtonsoftJson(options =>
6278
options.SerializerSettings.ContractResolver = new DefaultContractResolver());

Telerik.Examples.Mvc/Telerik.Examples.Mvc/Telerik.Examples.Mvc.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
<ItemGroup>
88
<PackageReference Include="AutoMapper" Version="14.0.0" />
9+
<PackageReference Include="Azure.AI.OpenAI" Version="2.8.0-beta.1" />
910
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.2" />
1011
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.2" />
1112
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.2" />
@@ -18,11 +19,14 @@
1819
<PrivateAssets>all</PrivateAssets>
1920
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2021
</PackageReference>
22+
<PackageReference Include="Microsoft.Extensions.AI" Version="10.1.1" />
23+
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.1.1-preview.1.25612.2" />
2124
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.2" />
2225
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
2326
<PackageReference Include="System.Drawing.Common" Version="9.0.2" />
2427
<PackageReference Include="System.Net.Http" Version="4.3.4" />
2528
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
29+
<PackageReference Include="Telerik.AI.SmartComponents.Extensions" Version="2.0.0" />
2630
<PackageReference Include="Telerik.Core.Export" Version="2025.3.812" />
2731
<PackageReference Include="Telerik.UI.for.AspNet.Core" Version="2025.4.1111" />
2832
<PackageReference Include="Telerik.Web.Captcha" Version="2.0.3" />

Telerik.Examples.Mvc/Telerik.Examples.Mvc/Views/Grid/SmartGrid.cshtml

Lines changed: 22 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77
@(Html.Kendo().Grid<SaleRecord>()
88
.Name("salesGrid")
9+
.ToolBar(t =>
10+
{
11+
t.AIAssistant();
12+
t.Spacer();
13+
t.Custom().Name("resetChanges")
14+
.Text("Reset changes")
15+
.IconClass("k-icon k-i-arrow-rotate-ccw");
16+
})
917
.Columns(columns =>
1018
{
1119
columns.Bound(c => c.Id).Width(80);
@@ -17,137 +25,22 @@
1725
})
1826
.Scrollable(s => s.Height("300px"))
1927
.DataSource(ds => ds.Ajax()
28+
.Model(m => m.Id(x => x.Id))
2029
.Read(read => read.Url("/SmartGrid/GetSales"))
2130
)
31+
.AI(ai => ai
32+
.Service("/SmartGrid/Analyze")
33+
.AIAssistant(aiAsst => aiAsst
34+
.PromptSuggestions(new[]
35+
{
36+
"Sort the grid by Total descending.",
37+
"Filter to only show data from July.",
38+
"Show only rows where Units Sold is greater than 130.",
39+
})
40+
.PromptTextArea(p => p.Rows(2).Resize(TextAreaResize.Auto).MaxRows(5))
41+
)
42+
.AIAssistantWindow(ws => ws.Width(558).Actions(a => a.Minimize().Close()))
43+
)
2244
.Sortable()
2345
.Filterable()
2446
)
25-
26-
<div class="k-mt-4" style="color: #ffffff;">
27-
<label for="instructionBox" class="k-label">Ask AI about the data:</label>
28-
29-
<ul style="margin-top: 5px; list-style: disc; padding-left: 20px; font-size: 14px; color: #00ffe5;">
30-
<li class="ai-suggestion">Which salesperson has the highest total sales?</li>
31-
<li class="ai-suggestion">What is the average units sold per region?</li>
32-
<li class="ai-suggestion">Show the month with the lowest performance.</li>
33-
<li class="ai-suggestion">Sort the grid by Total descending</li>
34-
<li class="ai-suggestion">Filter to only show data from July</li>
35-
<li class="ai-suggestion">Show only rows where Units Sold is greater than 130</li>
36-
<li class="ai-suggestion">What is the total revenue by month?</li>
37-
<li class="ai-suggestion">How many units did Alice sell in total?</li>
38-
</ul>
39-
40-
41-
@(Html.Kendo().TextArea()
42-
.Name("instructionBox")
43-
.Placeholder("e.g. Which region had the lowest total?")
44-
.Rows(4)
45-
.HtmlAttributes(new { style = "width:100%; background-color:#1f1f1f; color:white;", @class = "k-textbox" })
46-
)
47-
48-
<div class="k-mt-2">
49-
@(Html.Kendo().Button()
50-
.Name("analyzeButton")
51-
.Content("Analyze")
52-
.ThemeColor(ThemeColor.Success)
53-
.HtmlAttributes(new { onclick = "analyzeGrid()" })
54-
)
55-
</div>
56-
</div>
57-
58-
<div id="aiResponse" class="k-mt-4">
59-
<strong>AI Response:</strong>
60-
<div id="aiText" style="margin-top:10px;"></div>
61-
</div>
62-
63-
@section scripts {
64-
<script>
65-
$(document).on("click", ".ai-suggestion", function () {
66-
const text = $(this).text().trim();
67-
$("#instructionBox").data("kendoTextArea").value(text);
68-
});
69-
70-
71-
async function analyzeGrid() {
72-
const grid = $("#salesGrid").data("kendoGrid");
73-
const allData = grid.dataSource.view().map(item => item.toJSON());
74-
const instructions = $("#instructionBox").val();
75-
76-
if (!instructions.trim()) {
77-
alert("Please enter a question or instruction for the AI.");
78-
return;
79-
}
80-
81-
$("#aiText").html("<em>Thinking...</em>");
82-
83-
const response = await fetch('/SmartGrid/Analyze', {
84-
method: 'POST',
85-
headers: { 'Content-Type': 'application/json' },
86-
body: JSON.stringify({
87-
instructions: instructions,
88-
gridJson: JSON.stringify(allData)
89-
})
90-
});
91-
92-
const result = await response.json();
93-
94-
try {
95-
const json = JSON.parse(result.result);
96-
97-
if (json.action === "filter") {
98-
grid.dataSource.filter({
99-
field: json.field,
100-
operator: json.operator,
101-
value: json.value
102-
});
103-
$("#aiText").html(`<strong>Applied filter:</strong> ${json.field} ${json.operator} ${json.value}`);
104-
} else if (json.action === "sort") {
105-
grid.dataSource.sort({
106-
field: json.field,
107-
dir: json.dir
108-
});
109-
$("#aiText").html(`<strong>Sorted by:</strong> ${json.field} (${json.dir})`);
110-
} else {
111-
$("#aiText").text(result.result);
112-
}
113-
} catch {
114-
$("#aiText").text(result.result);
115-
}
116-
}
117-
</script>
118-
}
119-
120-
<style>
121-
textarea.k-textbox,
122-
.k-input {
123-
background-color: #1f1f1f;
124-
color: #ffffff;
125-
border-color: #444;
126-
}
127-
128-
textarea.k-textbox::placeholder {
129-
color: #888888;
130-
}
131-
132-
#aiResponse {
133-
background-color: #1f1f1f;
134-
border: 1px solid #444;
135-
padding: 1em;
136-
border-radius: 6px;
137-
margin-top: 1em;
138-
color: #00ffe5;
139-
font-size: 14px;
140-
}
141-
142-
#aiResponse strong {
143-
display: block;
144-
margin-bottom: 6px;
145-
color: #ffffff;
146-
font-weight: 600;
147-
font-size: 15px;
148-
}
149-
150-
#aiText {
151-
white-space: pre-line;
152-
}
153-
</style>

0 commit comments

Comments
 (0)