From 1c28f098ade128e455ae77fafcc054850f6076bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marker=20dao=20=C2=AE?= Date: Fri, 9 Jan 2026 16:35:46 +0000 Subject: [PATCH 1/8] refactor(htmleditor && chat): Angular --- .../Angular/app/ai.service.ts | 33 ++++++++++ .../Angular/app/app.component.ts | 2 + .../Angular/app/app.service.ts | 28 ++------ .../AITextEditing/Angular/app/ai.service.ts | 64 +++++++++++++++++++ .../Angular/app/app.component.ts | 61 ++---------------- .../AITextEditing/Angular/app/app.service.ts | 12 ---- 6 files changed, 109 insertions(+), 91 deletions(-) create mode 100644 apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai.service.ts create mode 100644 apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai.service.ts diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai.service.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai.service.ts new file mode 100644 index 000000000000..774a600bb93b --- /dev/null +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { AzureOpenAI } from 'openai'; + +@Injectable() +export class AiService { + chatService: AzureOpenAI; + + AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', + }; + + constructor() { + this.chatService = new AzureOpenAI(this.AzureOpenAIConfig); + } + + async getAIResponse(messages: any[]) { + const params = { + messages, + model: this.AzureOpenAIConfig.deployment, + max_tokens: 1000, + temperature: 0.7, + }; + + const response = await this.chatService.chat.completions.create(params); + const data = { choices: response.choices }; + + return data.choices[0].message?.content; + } +} diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts index beff93d55d76..6488d0f2fbb5 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts @@ -7,6 +7,7 @@ import { Observable } from 'rxjs'; import { loadMessages } from 'devextreme-angular/common/core/localization'; import { DataSource } from 'devextreme-angular/common/data'; import { AppService } from './app.service'; +import { AiService } from './ai.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); @@ -107,5 +108,6 @@ bootstrapApplication(AppComponent, { providers: [ provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), AppService, + AiService, ], }); diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts index b3ee9fe224d7..48a51f0645d3 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core'; import { Observable, BehaviorSubject } from 'rxjs'; -import { AzureOpenAI } from 'openai'; import { unified } from 'unified'; import remarkParse from 'remark-parse'; import remarkRehype from 'remark-rehype'; @@ -8,21 +7,14 @@ import rehypeStringify from 'rehype-stringify'; import rehypeMinifyWhitespace from 'rehype-minify-whitespace'; import { type DxChatTypes } from 'devextreme-angular/ui/chat'; import { DataSource, CustomStore } from 'devextreme-angular/common/data'; +import { AiService } from './ai.service'; @Injectable({ providedIn: 'root', }) export class AppService { - chatService: AzureOpenAI; - - AzureOpenAIConfig = { - dangerouslyAllowBrowser: true, - deployment: 'gpt-4o-mini', - apiVersion: '2024-02-01', - endpoint: 'https://public-api.devexpress.com/demo-openai', - apiKey: 'DEMO', - }; + aiService: AiService; REGENERATION_TEXT = 'Regeneration...'; @@ -51,8 +43,8 @@ export class AppService { alertsSubject: BehaviorSubject = new BehaviorSubject([]); - constructor() { - this.chatService = new AzureOpenAI(this.AzureOpenAIConfig); + constructor(aiService: AiService) { + this.aiService = aiService; this.initDataSource(); this.typingUsersSubject.next([]); this.alertsSubject.next([]); @@ -99,17 +91,7 @@ export class AppService { } async getAIResponse(messages) { - const params = { - messages, - model: this.AzureOpenAIConfig.deployment, - max_tokens: 1000, - temperature: 0.7, - }; - - const response = await this.chatService.chat.completions.create(params); - const data = { choices: response.choices }; - - return data.choices[0].message?.content; + return this.aiService.getAIResponse(messages); } async processMessageSending(message) { diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai.service.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai.service.ts new file mode 100644 index 000000000000..01e1fa4561ea --- /dev/null +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai.service.ts @@ -0,0 +1,64 @@ +import { AzureOpenAI, OpenAI } from 'openai'; +import { Injectable } from '@angular/core'; +import { + AIIntegration, + type RequestParams, + type Response, +} from 'devextreme-angular/common/ai-integration'; + +type AIMessage = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionSystemMessageParam) & { + content: string; +}; + +const AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', +}; + +const aiService = new AzureOpenAI(AzureOpenAIConfig); + +async function getAIResponse(messages: AIMessage[], signal: AbortSignal) { + const params = { + messages, + model: AzureOpenAIConfig.deployment, + max_tokens: 1000, + temperature: 0.7, + }; + + const response = await aiService.chat.completions.create(params, { signal }); + const result = response.choices[0].message?.content; + + return result; +} + +const aiIntegration = new AIIntegration({ + sendRequest({ prompt }: RequestParams): Response { + const controller = new AbortController(); + const signal = controller.signal; + + const aiPrompt: AIMessage[] = [ + { role: 'system', content: prompt.system }, + { role: 'user', content: prompt.user }, + ]; + const promise = getAIResponse(aiPrompt, signal); + + const result: Response = { + promise, + abort: () => { + controller.abort(); + }, + }; + + return result; + }, +}); + +@Injectable() +export class AiService { + getAiIntegration() { + return aiIntegration; + } +} diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts index 98b7034a2bf3..d3cd25a24df0 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts @@ -1,13 +1,9 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core'; import { DxHtmlEditorModule, type DxHtmlEditorTypes } from 'devextreme-angular/ui/html-editor'; -import { - AIIntegration, - RequestParams, - Response, -} from 'devextreme-angular/common/ai-integration'; -import { AzureOpenAI, OpenAI } from 'openai'; +import type { AIIntegration } from 'devextreme-angular/common/ai-integration'; import { Service } from './app.service'; +import { AiService } from './ai.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); @@ -19,74 +15,27 @@ if (window && window.config?.packageConfigPaths) { modulePrefix = '/app'; } -type AIMessage = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionSystemMessageParam) & { - content: string; -}; - @Component({ selector: 'demo-app', templateUrl: `.${modulePrefix}/app.component.html`, styleUrls: [`.${modulePrefix}/app.component.css`], - providers: [Service], + providers: [Service, AiService], imports: [ DxHtmlEditorModule, ], }) export class AppComponent { - azureOpenAIConfig: any; - - aiService: AzureOpenAI; - aiIntegration: AIIntegration; extractKeywordsPrompt: DxHtmlEditorTypes.AICustomCommand['prompt']; valueContent: string; - constructor(service: Service) { - this.azureOpenAIConfig = service.getAzureOpenAIConfig(); + constructor(service: Service, aiService: AiService) { this.extractKeywordsPrompt = service.getPrompt(); this.valueContent = service.getMarkup(); - - this.aiService = new AzureOpenAI(this.azureOpenAIConfig); - this.aiIntegration = new AIIntegration({ - sendRequest: this.sendRequest.bind(this), - }); - } - - sendRequest({ prompt }: RequestParams): Response { - const controller = new AbortController(); - const signal = controller.signal; - - const aiPrompt: AIMessage[] = [ - { role: 'system', content: prompt.system }, - { role: 'user', content: prompt.user }, - ]; - const promise = this.getAIResponse(aiPrompt, signal); - - const result: Response = { - promise, - abort: () => { - controller.abort(); - }, - }; - - return result; - } - - async getAIResponse(messages: AIMessage[], signal: AbortSignal) { - const params = { - messages, - model: this.azureOpenAIConfig.deployment, - max_tokens: 1000, - temperature: 0.7, - }; - - const response = await this.aiService.chat.completions.create(params, { signal }); - const result = response.choices[0].message?.content; - - return result; + this.aiIntegration = aiService.getAiIntegration(); } } diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.service.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.service.ts index 5d1cf9975165..f04866495dcc 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.service.ts +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.service.ts @@ -1,14 +1,6 @@ import { Injectable } from '@angular/core'; import { type DxHtmlEditorTypes } from 'devextreme-angular/ui/html_editor'; -const AzureOpenAIConfig = { - dangerouslyAllowBrowser: true, - deployment: 'gpt-4o-mini', - apiVersion: '2024-02-01', - endpoint: 'https://public-api.devexpress.com/demo-openai', - apiKey: 'DEMO', -}; - const extractKeywordsPrompt: DxHtmlEditorTypes.AICustomCommand['prompt'] = () => 'Extract a list of keywords from the text and return it as a comma-separated string'; @@ -56,8 +48,4 @@ export class Service { getPrompt() { return extractKeywordsPrompt; } - - getAzureOpenAIConfig() { - return AzureOpenAIConfig; - } } From 00a7965e0ccf4ad79be3190083e858eed414b6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marker=20dao=20=C2=AE?= Date: Fri, 9 Jan 2026 17:03:39 +0000 Subject: [PATCH 2/8] refactor(htmleditor && chat): React --- .../AIAndChatbotIntegration/React/data.ts | 8 --- .../AIAndChatbotIntegration/React/service.ts | 41 +++++++++++++++ .../AIAndChatbotIntegration/React/useApi.ts | 34 +------------ .../AIAndChatbotIntegration/ReactJs/data.js | 7 --- .../ReactJs/service.js | 28 ++++++++++ .../AIAndChatbotIntegration/ReactJs/useApi.js | 25 +-------- .../HtmlEditor/AITextEditing/React/App.tsx | 3 +- .../HtmlEditor/AITextEditing/React/data.ts | 50 ------------------ .../HtmlEditor/AITextEditing/React/service.ts | 51 +++++++++++++++++++ .../HtmlEditor/AITextEditing/ReactJs/App.js | 3 +- .../HtmlEditor/AITextEditing/ReactJs/data.js | 38 -------------- .../AITextEditing/ReactJs/service.js | 38 ++++++++++++++ 12 files changed, 166 insertions(+), 160 deletions(-) create mode 100644 apps/demos/Demos/Chat/AIAndChatbotIntegration/React/service.ts create mode 100644 apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/service.js create mode 100644 apps/demos/Demos/HtmlEditor/AITextEditing/React/service.ts create mode 100644 apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/service.js diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/data.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/data.ts index d9a1a4edd8c1..9c7f55803aaa 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/data.ts +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/data.ts @@ -1,13 +1,5 @@ import type { ChatTypes } from 'devextreme-react/chat'; -export const AzureOpenAIConfig = { - dangerouslyAllowBrowser: true, - deployment: 'gpt-4o-mini', - apiVersion: '2024-02-01', - endpoint: 'https://public-api.devexpress.com/demo-openai', - apiKey: 'DEMO', -}; - export const REGENERATION_TEXT = 'Regeneration...'; export const CHAT_DISABLED_CLASS = 'chat-disabled'; export const ALERT_TIMEOUT = 1000 * 60; diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/service.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/service.ts new file mode 100644 index 000000000000..e75319bd4c5c --- /dev/null +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/service.ts @@ -0,0 +1,41 @@ +import { AzureOpenAI, OpenAI } from 'openai'; +import type { AIResponse } from 'devextreme/common/ai-integration'; + +type Message = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionAssistantMessageParam) & { + content: string; +}; + +export const AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', +}; + +const chatService = new AzureOpenAI(AzureOpenAIConfig); + +const wait = (delay: number): Promise => + new Promise((resolve): void => { + setTimeout(resolve, delay); + }); + +export async function getAIResponse(messages: Message[], delay?: number): Promise { + const params = { + messages, + model: AzureOpenAIConfig.deployment, + max_tokens: 1000, + temperature: 0.7, + }; + + const response = await chatService.chat.completions.create(params); + const data = { choices: response.choices }; + + if (delay) { + await wait(delay); + } + + return data.choices[0].message?.content ?? ''; +} + +export type { Message }; diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts index e6f4ff4d0261..6e2831241087 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts @@ -1,43 +1,13 @@ import { useCallback, useState } from 'react'; -import { AzureOpenAI, OpenAI } from 'openai'; import type { ChatTypes } from 'devextreme-react/chat'; import { CustomStore, DataSource } from 'devextreme-react/common/data'; -import type { AIResponse } from 'devextreme/common/ai-integration'; import { ALERT_TIMEOUT, assistant, - AzureOpenAIConfig, REGENERATION_TEXT, } from './data.ts'; - -type Message = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionAssistantMessageParam) & { - content: string; -}; - -const chatService = new AzureOpenAI(AzureOpenAIConfig); - -const wait = (delay: number): Promise => - new Promise((resolve): void => { - setTimeout(resolve, delay); - }); - -export async function getAIResponse(messages: Message[], delay?: number): Promise { - const params = { - messages, - model: AzureOpenAIConfig.deployment, - max_tokens: 1000, - temperature: 0.7, - }; - - const response = await chatService.chat.completions.create(params); - const data = { choices: response.choices }; - - if (delay) { - await wait(delay); - } - - return data.choices[0].message?.content ?? ''; -} +import { getAIResponse } from './service.ts'; +import type { Message } from './service.ts'; const store: ChatTypes.Message[] = []; diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/data.js b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/data.js index 87d1d12667b0..66a606df2258 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/data.js +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/data.js @@ -1,10 +1,3 @@ -export const AzureOpenAIConfig = { - dangerouslyAllowBrowser: true, - deployment: 'gpt-4o-mini', - apiVersion: '2024-02-01', - endpoint: 'https://public-api.devexpress.com/demo-openai', - apiKey: 'DEMO', -}; export const REGENERATION_TEXT = 'Regeneration...'; export const CHAT_DISABLED_CLASS = 'chat-disabled'; export const ALERT_TIMEOUT = 1000 * 60; diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/service.js b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/service.js new file mode 100644 index 000000000000..9ac847465eec --- /dev/null +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/service.js @@ -0,0 +1,28 @@ +import { AzureOpenAI } from 'openai'; + +export const AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', +}; +const chatService = new AzureOpenAI(AzureOpenAIConfig); +const wait = (delay) => + new Promise((resolve) => { + setTimeout(resolve, delay); + }); +export async function getAIResponse(messages, delay) { + const params = { + messages, + model: AzureOpenAIConfig.deployment, + max_tokens: 1000, + temperature: 0.7, + }; + const response = await chatService.chat.completions.create(params); + const data = { choices: response.choices }; + if (delay) { + await wait(delay); + } + return data.choices[0].message?.content ?? ''; +} diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/useApi.js b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/useApi.js index b8fdc6bedf9f..7f88abd8f651 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/useApi.js +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/useApi.js @@ -1,29 +1,8 @@ import { useCallback, useState } from 'react'; -import { AzureOpenAI } from 'openai'; import { CustomStore, DataSource } from 'devextreme-react/common/data'; -import { - ALERT_TIMEOUT, assistant, AzureOpenAIConfig, REGENERATION_TEXT, -} from './data.js'; +import { ALERT_TIMEOUT, assistant, REGENERATION_TEXT } from './data.js'; +import { getAIResponse } from './service.js'; -const chatService = new AzureOpenAI(AzureOpenAIConfig); -const wait = (delay) => - new Promise((resolve) => { - setTimeout(resolve, delay); - }); -export async function getAIResponse(messages, delay) { - const params = { - messages, - model: AzureOpenAIConfig.deployment, - max_tokens: 1000, - temperature: 0.7, - }; - const response = await chatService.chat.completions.create(params); - const data = { choices: response.choices }; - if (delay) { - await wait(delay); - } - return data.choices[0].message?.content ?? ''; -} const store = []; const customStore = new CustomStore({ key: 'id', diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/React/App.tsx b/apps/demos/Demos/HtmlEditor/AITextEditing/React/App.tsx index 9139ea28c982..7f4ec1f4ad57 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/React/App.tsx +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/React/App.tsx @@ -1,6 +1,7 @@ import React from 'react'; import HtmlEditor, { Toolbar, ToolbarItem, Command } from 'devextreme-react/html-editor'; -import { markup, extractKeywordsPrompt, aiIntegration } from './data.ts'; +import { markup, extractKeywordsPrompt } from './data.ts'; +import { aiIntegration } from './service.ts'; export default function App() { return ( diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/React/data.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/React/data.ts index 8bf5e56ccd39..a685e5a3d87f 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/React/data.ts +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/React/data.ts @@ -1,54 +1,4 @@ import type { HtmlEditorTypes } from 'devextreme-react/html-editor'; -import { AIIntegration } from 'devextreme-react/common/ai-integration'; -import type { AIResponse, RequestParams, Response } from 'devextreme-react/common/ai-integration'; -import { AzureOpenAI, OpenAI } from 'openai'; - -type AIMessage = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionSystemMessageParam) & { - content: string; -}; - -const AzureOpenAIConfig = { - dangerouslyAllowBrowser: true, - deployment: 'gpt-4o-mini', - apiVersion: '2024-02-01', - endpoint: 'https://public-api.devexpress.com/demo-openai', - apiKey: 'DEMO', -}; - -const aiService = new AzureOpenAI(AzureOpenAIConfig); - -async function getAIResponse(messages: AIMessage[], signal: AbortSignal) { - const params = { - messages, - model: AzureOpenAIConfig.deployment, - max_tokens: 1000, - temperature: 0.7, - }; - - const response = await aiService.chat.completions.create(params, { signal }); - return response.choices[0].message?.content; -} - -export const aiIntegration = new AIIntegration({ - sendRequest({ prompt }: RequestParams): Response { - const controller = new AbortController(); - const signal = controller.signal; - - const aiPrompt: AIMessage[] = [ - { role: 'system', content: prompt.system ?? '' }, - { role: 'user', content: prompt.user ?? '' }, - ]; - - const promise = getAIResponse(aiPrompt, signal) as Promise; - - return { - promise, - abort: () => { - controller.abort(); - }, - }; - }, -}); export const markup = `

diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/React/service.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/React/service.ts new file mode 100644 index 000000000000..c9ddc84ed899 --- /dev/null +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/React/service.ts @@ -0,0 +1,51 @@ +import { AIIntegration } from 'devextreme-react/common/ai-integration'; +import type { AIResponse, RequestParams, Response } from 'devextreme-react/common/ai-integration'; +import { AzureOpenAI, OpenAI } from 'openai'; + +type AIMessage = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionSystemMessageParam) & { + content: string; +}; + +const AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', +}; + +const aiService = new AzureOpenAI(AzureOpenAIConfig); + +async function getAIResponse(messages: AIMessage[], signal: AbortSignal) { + const params = { + messages, + model: AzureOpenAIConfig.deployment, + max_tokens: 1000, + temperature: 0.7, + }; + + const response = await aiService.chat.completions.create(params, { signal }); + + return response.choices[0].message?.content; +} + +export const aiIntegration = new AIIntegration({ + sendRequest({ prompt }: RequestParams): Response { + const controller = new AbortController(); + const signal = controller.signal; + + const aiPrompt: AIMessage[] = [ + { role: 'system', content: prompt.system ?? '' }, + { role: 'user', content: prompt.user ?? '' }, + ]; + + const promise = getAIResponse(aiPrompt, signal) as Promise; + + return { + promise, + abort: () => { + controller.abort(); + }, + }; + }, +}); diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/App.js b/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/App.js index 982bbbbdf6ed..8a2545031306 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/App.js +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/App.js @@ -1,6 +1,7 @@ import React from 'react'; import HtmlEditor, { Toolbar, ToolbarItem, Command } from 'devextreme-react/html-editor'; -import { markup, extractKeywordsPrompt, aiIntegration } from './data.js'; +import { markup, extractKeywordsPrompt } from './data.js'; +import { aiIntegration } from './service.js'; export default function App() { return ( diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/data.js b/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/data.js index f7d79dab710f..160cecb3c1f8 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/data.js +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/data.js @@ -1,41 +1,3 @@ -import { AIIntegration } from 'devextreme-react/common/ai-integration'; -import { AzureOpenAI } from 'openai'; - -const AzureOpenAIConfig = { - dangerouslyAllowBrowser: true, - deployment: 'gpt-4o-mini', - apiVersion: '2024-02-01', - endpoint: 'https://public-api.devexpress.com/demo-openai', - apiKey: 'DEMO', -}; -const aiService = new AzureOpenAI(AzureOpenAIConfig); -async function getAIResponse(messages, signal) { - const params = { - messages, - model: AzureOpenAIConfig.deployment, - max_tokens: 1000, - temperature: 0.7, - }; - const response = await aiService.chat.completions.create(params, { signal }); - return response.choices[0].message?.content; -} -export const aiIntegration = new AIIntegration({ - sendRequest({ prompt }) { - const controller = new AbortController(); - const signal = controller.signal; - const aiPrompt = [ - { role: 'system', content: prompt.system ?? '' }, - { role: 'user', content: prompt.user ?? '' }, - ]; - const promise = getAIResponse(aiPrompt, signal); - return { - promise, - abort: () => { - controller.abort(); - }, - }; - }, -}); export const markup = `

HtmlEditor diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/service.js b/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/service.js new file mode 100644 index 000000000000..bbbf44050c1c --- /dev/null +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/ReactJs/service.js @@ -0,0 +1,38 @@ +import { AIIntegration } from 'devextreme-react/common/ai-integration'; +import { AzureOpenAI } from 'openai'; + +const AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', +}; +const aiService = new AzureOpenAI(AzureOpenAIConfig); +async function getAIResponse(messages, signal) { + const params = { + messages, + model: AzureOpenAIConfig.deployment, + max_tokens: 1000, + temperature: 0.7, + }; + const response = await aiService.chat.completions.create(params, { signal }); + return response.choices[0].message?.content; +} +export const aiIntegration = new AIIntegration({ + sendRequest({ prompt }) { + const controller = new AbortController(); + const signal = controller.signal; + const aiPrompt = [ + { role: 'system', content: prompt.system ?? '' }, + { role: 'user', content: prompt.user ?? '' }, + ]; + const promise = getAIResponse(aiPrompt, signal); + return { + promise, + abort: () => { + controller.abort(); + }, + }; + }, +}); From e2ffd032a790c037a5502f7cefb780e384b7d3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marker=20dao=20=C2=AE?= Date: Fri, 9 Jan 2026 17:07:12 +0000 Subject: [PATCH 3/8] fix(demos): Move files to the ai folder --- .../AIAndChatbotIntegration/Angular/app/{ => ai}/ai.service.ts | 0 .../Chat/AIAndChatbotIntegration/Angular/app/app.component.ts | 2 +- .../Chat/AIAndChatbotIntegration/Angular/app/app.service.ts | 2 +- .../HtmlEditor/AITextEditing/Angular/app/{ => ai}/ai.service.ts | 0 .../Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/{ => ai}/ai.service.ts (100%) rename apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/{ => ai}/ai.service.ts (100%) diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai.service.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai/ai.service.ts similarity index 100% rename from apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai.service.ts rename to apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai/ai.service.ts diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts index 6488d0f2fbb5..40236d268098 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts @@ -7,7 +7,7 @@ import { Observable } from 'rxjs'; import { loadMessages } from 'devextreme-angular/common/core/localization'; import { DataSource } from 'devextreme-angular/common/data'; import { AppService } from './app.service'; -import { AiService } from './ai.service'; +import { AiService } from './ai/ai.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts index 48a51f0645d3..98517ea729bf 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts @@ -7,7 +7,7 @@ import rehypeStringify from 'rehype-stringify'; import rehypeMinifyWhitespace from 'rehype-minify-whitespace'; import { type DxChatTypes } from 'devextreme-angular/ui/chat'; import { DataSource, CustomStore } from 'devextreme-angular/common/data'; -import { AiService } from './ai.service'; +import { AiService } from './ai/ai.service'; @Injectable({ providedIn: 'root', diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai.service.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai/ai.service.ts similarity index 100% rename from apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai.service.ts rename to apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/ai/ai.service.ts diff --git a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts index d3cd25a24df0..c3df9af5fa05 100644 --- a/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts +++ b/apps/demos/Demos/HtmlEditor/AITextEditing/Angular/app/app.component.ts @@ -3,7 +3,7 @@ import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/ import { DxHtmlEditorModule, type DxHtmlEditorTypes } from 'devextreme-angular/ui/html-editor'; import type { AIIntegration } from 'devextreme-angular/common/ai-integration'; import { Service } from './app.service'; -import { AiService } from './ai.service'; +import { AiService } from './ai/ai.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); From d15a166b4d8d713057b818ac919435817bc505c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marker=20dao=20=C2=AE?= Date: Fri, 9 Jan 2026 17:24:49 +0000 Subject: [PATCH 4/8] refactor(htmleditor && chat): Vue --- .../Chat/AIAndChatbotIntegration/Vue/App.vue | 19 +------ .../Chat/AIAndChatbotIntegration/Vue/data.ts | 8 --- .../AIAndChatbotIntegration/Vue/service.ts | 26 ++++++++++ .../HtmlEditor/AITextEditing/Vue/App.vue | 49 +---------------- .../HtmlEditor/AITextEditing/Vue/service.ts | 52 +++++++++++++++++++ 5 files changed, 80 insertions(+), 74 deletions(-) create mode 100644 apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/service.ts create mode 100644 apps/demos/Demos/HtmlEditor/AITextEditing/Vue/service.ts diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue index b36dba52528a..53cbc18ef9b5 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue @@ -49,7 +49,6 @@