diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai/ai.service.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai/ai.service.ts new file mode 100644 index 000000000000..e634e0f45061 --- /dev/null +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/ai/ai.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { AzureOpenAI, OpenAI } from 'openai'; +import { type AIResponse } from 'devextreme-angular/common/ai-integration'; + +export type AIMessage = ( + OpenAI.ChatCompletionUserMessageParam + | OpenAI.ChatCompletionSystemMessageParam + | OpenAI.ChatCompletionAssistantMessageParam) & { + content: string; + }; + +const AzureOpenAIConfig = { + dangerouslyAllowBrowser: true, + deployment: 'gpt-4o-mini', + apiVersion: '2024-02-01', + endpoint: 'https://public-api.devexpress.com/demo-openai', + apiKey: 'DEMO', +}; + +@Injectable() +export class AiService { + chatService: AzureOpenAI; + + constructor() { + this.chatService = new AzureOpenAI(AzureOpenAIConfig); + } + + async getAIResponse(messages: AIMessage[]): Promise { + const params = { + messages, + model: 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..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,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/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..29b7fdf00957 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, type AIMessage } from './ai/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...'; @@ -39,7 +31,7 @@ export class AppService { store: any[] = []; - messages: any[] = []; + messages: AIMessage[] = []; alerts: DxChatTypes.Alert[] = []; @@ -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([]); @@ -98,21 +90,11 @@ 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; + async getAIResponse(messages: AIMessage[]): Promise { + return this.aiService.getAIResponse(messages) as Promise; } - async processMessageSending(message) { + async processMessageSending(message: DxChatTypes.Message) { this.messages.push({ role: 'user', content: message.text }); this.typingUsersSubject.next([this.assistant]); 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..b668c1eb5e73 --- /dev/null +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/service.ts @@ -0,0 +1,42 @@ +import { AzureOpenAI, OpenAI } from 'openai'; +import { type AIResponse } from 'devextreme-react/common/ai-integration'; + +export type AIMessage = ( + OpenAI.ChatCompletionUserMessageParam + | OpenAI.ChatCompletionSystemMessageParam + | OpenAI.ChatCompletionAssistantMessageParam) & { + 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 chatService = new AzureOpenAI(AzureOpenAIConfig); + +const wait = (delay: number): Promise => + new Promise((resolve): void => { + setTimeout(resolve, delay); + }); + +export async function getAIResponse(messages: AIMessage[], 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 ?? ''; +} diff --git a/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts b/apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts index e6f4ff4d0261..03a20011b0db 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 { AIMessage } from './service.ts'; const store: ChatTypes.Message[] = []; @@ -61,12 +31,12 @@ export const dataSource = new DataSource({ paginate: false, }); -const dataItemToMessage = (item: ChatTypes.Message): Message => ({ - role: item.author?.id as Message['role'], +const dataItemToMessage = (item: ChatTypes.Message): AIMessage => ({ + role: item.author?.id as AIMessage['role'], content: item.text, }); -const getMessageHistory = (): Message[] => [...dataSource.items()].map(dataItemToMessage); +const getMessageHistory = (): AIMessage[] => [...dataSource.items()].map(dataItemToMessage); export const useApi = () => { const [alerts, setAlerts] = useState([]); 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..11528bfb03c6 --- /dev/null +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/service.js @@ -0,0 +1,28 @@ +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 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/Chat/AIAndChatbotIntegration/Vue/App.vue b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue index b36dba52528a..ffd533ed4968 100644 --- a/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue +++ b/apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue @@ -49,7 +49,6 @@