Skip to content

Commit c1073ff

Browse files
committed
add ai integration examples for scheduler, grid, editor, and chart telerik/kendo#24269
1 parent d6284df commit c1073ff

File tree

14 files changed

+1093
-57
lines changed

14 files changed

+1093
-57
lines changed
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
using Microsoft.Extensions.Configuration;
2+
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Linq;
4+
using System;
5+
using System.Net.Http;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Telerik.Examples.Mvc.Controllers
10+
{
11+
public class AiService
12+
{
13+
private readonly HttpClient _http;
14+
private readonly string _apiKey;
15+
private readonly string _endpoint;
16+
private readonly string _deployment;
17+
18+
public AiService(IConfiguration config)
19+
{
20+
_http = new HttpClient();
21+
_apiKey = config["OpenAI:ApiKey"];
22+
_endpoint = config["OpenAI:Endpoint"];
23+
_deployment = config["OpenAI:DeploymentName"];
24+
}
25+
26+
public async Task<string> ProcessAsync(string prompt)
27+
{
28+
var url = $"{_endpoint}openai/deployments/{_deployment}/chat/completions?api-version=2024-02-15-preview";
29+
var payload = new
30+
{
31+
messages = new[]
32+
{
33+
new { role = "system", content = "You are a helpful assistant." },
34+
new { role = "user", content = prompt }
35+
},
36+
temperature = 0.3,
37+
max_tokens = 1500
38+
};
39+
40+
_http.DefaultRequestHeaders.Clear();
41+
_http.DefaultRequestHeaders.Add("api-key", _apiKey);
42+
43+
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
44+
var response = await _http.PostAsync(url, content);
45+
var text = await response.Content.ReadAsStringAsync();
46+
47+
if (!response.IsSuccessStatusCode)
48+
return $"Azure OpenAI API error: {response.StatusCode}";
49+
50+
var json = JObject.Parse(text);
51+
return json["choices"]?[0]?["message"]?["content"]?.ToString()?.Trim() ?? "";
52+
}
53+
54+
public async Task<string> AnalyzeGridDataAsync(string instructions, string gridDataJson)
55+
{
56+
var systemPrompt = @"
57+
You are an AI assistant analyzing Kendo UI Grid data.
58+
59+
You ALWAYS receive:
60+
1) The full raw data (JSON array)
61+
2) A user question
62+
63+
RULES:
64+
1. If the user explicitly asks for filtering or sorting:
65+
Return ONLY a JSON object such as:
66+
{ ""action"": ""filter"", ""field"": ""Month"", ""operator"": ""eq"", ""value"": ""July"" }
67+
68+
or:
69+
70+
{ ""action"": ""sort"", ""field"": ""Total"", ""dir"": ""desc"" }
71+
72+
2. For ALL OTHER QUESTIONS:
73+
Return a short natural language answer using ONLY the supplied data.
74+
75+
No code, no markdown, no explanations.
76+
";
77+
78+
try
79+
{
80+
var url = $"{_endpoint}openai/deployments/{_deployment}/chat/completions?api-version=2024-02-15-preview";
81+
82+
var payload = new
83+
{
84+
messages = new[]
85+
{
86+
new { role = "system", content = systemPrompt },
87+
new { role = "user", content = $"Grid Data:\n{gridDataJson}" },
88+
new { role = "user", content = $"Question:\n{instructions}" }
89+
},
90+
temperature = 0.3,
91+
max_tokens = 1500
92+
};
93+
94+
_http.DefaultRequestHeaders.Clear();
95+
_http.DefaultRequestHeaders.Add("api-key", _apiKey);
96+
97+
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
98+
var response = await _http.PostAsync(url, content);
99+
var text = await response.Content.ReadAsStringAsync();
100+
101+
if (!response.IsSuccessStatusCode)
102+
return $"Azure OpenAI API error: {response.StatusCode}";
103+
104+
var json = JObject.Parse(text);
105+
return json["choices"]?[0]?["message"]?["content"]?.ToString()?.Trim() ?? "";
106+
}
107+
catch
108+
{
109+
return "AI configuration missing";
110+
}
111+
}
112+
public async Task<string> EditTextAsync(string text, string instruction)
113+
{
114+
try
115+
{
116+
var url = $"{_endpoint}openai/deployments/{_deployment}/chat/completions?api-version=2024-02-15-preview";
117+
118+
var payload = new
119+
{
120+
messages = new[]
121+
{
122+
new { role = "system", content =
123+
"You are an AI text editor. Modify the text ONLY according to the user's instruction while preserving existing formatting. " +
124+
"If the user requests color, bold, or styles, return the text wrapped in proper HTML inline styles (e.g., <span style='color: green;'>text</span>). " +
125+
"Do not apply additional formatting unless explicitly requested."
126+
},
127+
new { role = "user", content =
128+
$"Modify this text:\n\n{text}\n\nInstruction: {instruction}\n\nReturn the modified text as valid HTML with inline styles only if formatting changes are required."
129+
}
130+
},
131+
temperature = 0.3,
132+
max_tokens = 500
133+
};
134+
135+
_http.DefaultRequestHeaders.Clear();
136+
_http.DefaultRequestHeaders.Add("api-key", _apiKey);
137+
138+
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
139+
var response = await _http.PostAsync(url, content);
140+
var responseText = await response.Content.ReadAsStringAsync();
141+
142+
if (!response.IsSuccessStatusCode)
143+
return $"Azure OpenAI API error: {response.StatusCode} - {responseText}";
144+
145+
var json = JObject.Parse(responseText);
146+
return json["choices"]?[0]?["message"]?["content"]?.ToString()?.Trim() ?? "No AI response.";
147+
}
148+
catch
149+
{
150+
return "AI configuration missing";
151+
}
152+
}
153+
154+
public async Task<string> GenerateChartConfigAsync(string instructions)
155+
{
156+
var url = $"{_endpoint}openai/deployments/{_deployment}/chat/completions?api-version=2024-02-15-preview";
157+
158+
var payload = new
159+
{
160+
messages = new[]
161+
{
162+
new { role = "system", content = "Return ONLY valid JSON for a Kendo UI Chart." },
163+
new { role = "user", content = $"Generate a Kendo UI Chart JSON configuration. Instructions: {instructions}" }
164+
},
165+
temperature = 0.2,
166+
max_tokens = 600
167+
};
168+
169+
_http.DefaultRequestHeaders.Clear();
170+
_http.DefaultRequestHeaders.Add("api-key", _apiKey);
171+
172+
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
173+
var response = await _http.PostAsync(url, content);
174+
175+
if (!response.IsSuccessStatusCode)
176+
return "{}";
177+
178+
var raw = await response.Content.ReadAsStringAsync();
179+
dynamic json = JsonConvert.DeserializeObject(raw);
180+
181+
string config = json?.choices?[0]?.message?.content?.ToString() ?? "";
182+
if (string.IsNullOrWhiteSpace(config))
183+
return "{}";
184+
185+
config = config.Trim();
186+
187+
int start = config.IndexOf('{');
188+
int end = config.LastIndexOf('}');
189+
if (start >= 0 && end > start)
190+
config = config.Substring(start, end - start + 1);
191+
192+
try
193+
{
194+
JObject.Parse(config);
195+
return config;
196+
}
197+
catch
198+
{
199+
config = config.Replace(",]", "]").Replace(",}", "}");
200+
try
201+
{
202+
JObject.Parse(config);
203+
return config;
204+
}
205+
catch
206+
{
207+
return "{}";
208+
}
209+
}
210+
}
211+
212+
public async Task<string> GenerateSchedulerConfigAsync(string instructions)
213+
{
214+
var url = $"{_endpoint}openai/deployments/{_deployment}/chat/completions?api-version=2024-02-15-preview";
215+
216+
var systemPrompt = @"
217+
Return ONLY valid JSON for a Kendo UI Scheduler.
218+
219+
The JSON object MUST contain:
220+
{
221+
""date"": ""2025-01-10T00:00:00Z"", // optional
222+
""events"": [
223+
{
224+
""id"": 1,
225+
""title"": ""Design Review"",
226+
""start"": ""2025-01-10T10:00:00Z"",
227+
""end"": ""2025-01-10T11:00:00Z""
228+
}
229+
]
230+
}
231+
232+
RULES:
233+
- Convert all dates into ISO UTC strings.
234+
- The 'events' array must contain the tasks created or modified.
235+
- Do NOT include Kendo configuration like views or transport.
236+
";
237+
238+
var payload = new
239+
{
240+
messages = new[]
241+
{
242+
new { role = "system", content = systemPrompt },
243+
new { role = "user", content = instructions }
244+
},
245+
temperature = 0.2,
246+
max_tokens = 600
247+
};
248+
249+
_http.DefaultRequestHeaders.Clear();
250+
_http.DefaultRequestHeaders.Add("api-key", _apiKey);
251+
252+
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
253+
var response = await _http.PostAsync(url, content);
254+
255+
var raw = await response.Content.ReadAsStringAsync();
256+
if (!response.IsSuccessStatusCode)
257+
return "{}";
258+
259+
dynamic json = JsonConvert.DeserializeObject(raw);
260+
string config = json?.choices?[0]?.message?.content?.ToString() ?? "{}";
261+
262+
config = config.Trim();
263+
264+
int start = config.IndexOf("{");
265+
int end = config.LastIndexOf("}");
266+
if (start >= 0 && end > start)
267+
config = config.Substring(start, end - start + 1);
268+
269+
try
270+
{
271+
JObject.Parse(config);
272+
return config;
273+
}
274+
catch
275+
{
276+
config = config.Replace(",]", "]").Replace(",}", "}");
277+
try
278+
{
279+
JObject.Parse(config);
280+
return config;
281+
}
282+
catch
283+
{
284+
return "{}";
285+
}
286+
}
287+
}
288+
}
289+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.Extensions.Logging;
3+
using System;
4+
using System.Threading.Tasks;
5+
6+
namespace Telerik.Examples.Mvc.Controllers.Chart
7+
{
8+
public class SmartChartController : Controller
9+
{
10+
private readonly AiService _chartService;
11+
private readonly ILogger<SmartChartController> _logger;
12+
13+
public SmartChartController(AiService chartService, ILogger<SmartChartController> logger)
14+
{
15+
_chartService = chartService;
16+
_logger = logger;
17+
}
18+
19+
[HttpPost]
20+
[Route("SmartChart/GenerateChart")]
21+
public async Task<IActionResult> GenerateChart([FromBody] ChartRequest request)
22+
{
23+
if (string.IsNullOrWhiteSpace(request.Instructions))
24+
return BadRequest("Instructions cannot be empty.");
25+
26+
try
27+
{
28+
var chartConfig = await _chartService.GenerateChartConfigAsync(request.Instructions);
29+
return Json(new { config = chartConfig });
30+
}
31+
catch (Exception ex)
32+
{
33+
_logger.LogError(ex, "Error generating chart");
34+
return StatusCode(500, new { error = ex.Message });
35+
}
36+
}
37+
38+
[HttpGet]
39+
public IActionResult SmartChart()
40+
{
41+
return View();
42+
}
43+
}
44+
45+
public class ChartRequest
46+
{
47+
public string Instructions { get; set; }
48+
}
49+
}

Telerik.Examples.Mvc/Telerik.Examples.Mvc/Controllers/Chat/AiService.cs

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)