Skip to content

Commit 95abd55

Browse files
marker-daomarker dao ®
andauthored
HtmlEditor and Chat AI Demos: Create an AI service to make demo code more readable (#32154)
Co-authored-by: marker dao ® <youdontknow@marker-dao.eth>
1 parent a776d7d commit 95abd55

24 files changed

Lines changed: 390 additions & 343 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Injectable } from '@angular/core';
2+
import { AzureOpenAI, OpenAI } from 'openai';
3+
import { type AIResponse } from 'devextreme-angular/common/ai-integration';
4+
5+
export type AIMessage = (
6+
OpenAI.ChatCompletionUserMessageParam
7+
| OpenAI.ChatCompletionSystemMessageParam
8+
| OpenAI.ChatCompletionAssistantMessageParam) & {
9+
content: string;
10+
};
11+
12+
const AzureOpenAIConfig = {
13+
dangerouslyAllowBrowser: true,
14+
deployment: 'gpt-4o-mini',
15+
apiVersion: '2024-02-01',
16+
endpoint: 'https://public-api.devexpress.com/demo-openai',
17+
apiKey: 'DEMO',
18+
};
19+
20+
@Injectable()
21+
export class AiService {
22+
chatService: AzureOpenAI;
23+
24+
constructor() {
25+
this.chatService = new AzureOpenAI(AzureOpenAIConfig);
26+
}
27+
28+
async getAIResponse(messages: AIMessage[]): Promise<AIResponse> {
29+
const params = {
30+
messages,
31+
model: AzureOpenAIConfig.deployment,
32+
max_tokens: 1000,
33+
temperature: 0.7,
34+
};
35+
36+
const response = await this.chatService.chat.completions.create(params);
37+
const data = { choices: response.choices };
38+
39+
return data.choices[0].message?.content;
40+
}
41+
}

apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Observable } from 'rxjs';
77
import { loadMessages } from 'devextreme-angular/common/core/localization';
88
import { DataSource } from 'devextreme-angular/common/data';
99
import { AppService } from './app.service';
10+
import { AiService } from './ai/ai.service';
1011

1112
if (!/localhost/.test(document.location.host)) {
1213
enableProdMode();
@@ -107,5 +108,6 @@ bootstrapApplication(AppComponent, {
107108
providers: [
108109
provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }),
109110
AppService,
111+
AiService,
110112
],
111113
});

apps/demos/Demos/Chat/AIAndChatbotIntegration/Angular/app/app.service.ts

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
import { Injectable } from '@angular/core';
22
import { Observable, BehaviorSubject } from 'rxjs';
3-
import { AzureOpenAI } from 'openai';
43
import { unified } from 'unified';
54
import remarkParse from 'remark-parse';
65
import remarkRehype from 'remark-rehype';
76
import rehypeStringify from 'rehype-stringify';
87
import rehypeMinifyWhitespace from 'rehype-minify-whitespace';
98
import { type DxChatTypes } from 'devextreme-angular/ui/chat';
109
import { DataSource, CustomStore } from 'devextreme-angular/common/data';
10+
import { AiService, type AIMessage } from './ai/ai.service';
1111

1212
@Injectable({
1313
providedIn: 'root',
1414
})
1515

1616
export class AppService {
17-
chatService: AzureOpenAI;
18-
19-
AzureOpenAIConfig = {
20-
dangerouslyAllowBrowser: true,
21-
deployment: 'gpt-4o-mini',
22-
apiVersion: '2024-02-01',
23-
endpoint: 'https://public-api.devexpress.com/demo-openai',
24-
apiKey: 'DEMO',
25-
};
17+
aiService: AiService;
2618

2719
REGENERATION_TEXT = 'Regeneration...';
2820

@@ -39,7 +31,7 @@ export class AppService {
3931

4032
store: any[] = [];
4133

42-
messages: any[] = [];
34+
messages: AIMessage[] = [];
4335

4436
alerts: DxChatTypes.Alert[] = [];
4537

@@ -51,8 +43,8 @@ export class AppService {
5143

5244
alertsSubject: BehaviorSubject<DxChatTypes.Alert[]> = new BehaviorSubject([]);
5345

54-
constructor() {
55-
this.chatService = new AzureOpenAI(this.AzureOpenAIConfig);
46+
constructor(aiService: AiService) {
47+
this.aiService = aiService;
5648
this.initDataSource();
5749
this.typingUsersSubject.next([]);
5850
this.alertsSubject.next([]);
@@ -98,21 +90,11 @@ export class AppService {
9890
});
9991
}
10092

101-
async getAIResponse(messages) {
102-
const params = {
103-
messages,
104-
model: this.AzureOpenAIConfig.deployment,
105-
max_tokens: 1000,
106-
temperature: 0.7,
107-
};
108-
109-
const response = await this.chatService.chat.completions.create(params);
110-
const data = { choices: response.choices };
111-
112-
return data.choices[0].message?.content;
93+
async getAIResponse(messages: AIMessage[]): Promise<string> {
94+
return this.aiService.getAIResponse(messages) as Promise<string>;
11395
}
11496

115-
async processMessageSending(message) {
97+
async processMessageSending(message: DxChatTypes.Message) {
11698
this.messages.push({ role: 'user', content: message.text });
11799
this.typingUsersSubject.next([this.assistant]);
118100

apps/demos/Demos/Chat/AIAndChatbotIntegration/React/data.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
import type { ChatTypes } from 'devextreme-react/chat';
22

3-
export const AzureOpenAIConfig = {
4-
dangerouslyAllowBrowser: true,
5-
deployment: 'gpt-4o-mini',
6-
apiVersion: '2024-02-01',
7-
endpoint: 'https://public-api.devexpress.com/demo-openai',
8-
apiKey: 'DEMO',
9-
};
10-
113
export const REGENERATION_TEXT = 'Regeneration...';
124
export const CHAT_DISABLED_CLASS = 'chat-disabled';
135
export const ALERT_TIMEOUT = 1000 * 60;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { AzureOpenAI, OpenAI } from 'openai';
2+
import { type AIResponse } from 'devextreme-react/common/ai-integration';
3+
4+
export type AIMessage = (
5+
OpenAI.ChatCompletionUserMessageParam
6+
| OpenAI.ChatCompletionSystemMessageParam
7+
| OpenAI.ChatCompletionAssistantMessageParam) & {
8+
content: string;
9+
};
10+
11+
const AzureOpenAIConfig = {
12+
dangerouslyAllowBrowser: true,
13+
deployment: 'gpt-4o-mini',
14+
apiVersion: '2024-02-01',
15+
endpoint: 'https://public-api.devexpress.com/demo-openai',
16+
apiKey: 'DEMO',
17+
};
18+
19+
const chatService = new AzureOpenAI(AzureOpenAIConfig);
20+
21+
const wait = (delay: number): Promise<void> =>
22+
new Promise((resolve): void => {
23+
setTimeout(resolve, delay);
24+
});
25+
26+
export async function getAIResponse(messages: AIMessage[], delay?: number): Promise<AIResponse> {
27+
const params = {
28+
messages,
29+
model: AzureOpenAIConfig.deployment,
30+
max_tokens: 1000,
31+
temperature: 0.7,
32+
};
33+
34+
const response = await chatService.chat.completions.create(params);
35+
const data = { choices: response.choices };
36+
37+
if (delay) {
38+
await wait(delay);
39+
}
40+
41+
return data.choices[0].message?.content ?? '';
42+
}

apps/demos/Demos/Chat/AIAndChatbotIntegration/React/useApi.ts

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,13 @@
11
import { useCallback, useState } from 'react';
2-
import { AzureOpenAI, OpenAI } from 'openai';
32
import type { ChatTypes } from 'devextreme-react/chat';
43
import { CustomStore, DataSource } from 'devextreme-react/common/data';
5-
import type { AIResponse } from 'devextreme/common/ai-integration';
64
import {
75
ALERT_TIMEOUT,
86
assistant,
9-
AzureOpenAIConfig,
107
REGENERATION_TEXT,
118
} from './data.ts';
12-
13-
type Message = (OpenAI.ChatCompletionUserMessageParam | OpenAI.ChatCompletionAssistantMessageParam) & {
14-
content: string;
15-
};
16-
17-
const chatService = new AzureOpenAI(AzureOpenAIConfig);
18-
19-
const wait = (delay: number): Promise<void> =>
20-
new Promise((resolve): void => {
21-
setTimeout(resolve, delay);
22-
});
23-
24-
export async function getAIResponse(messages: Message[], delay?: number): Promise<AIResponse> {
25-
const params = {
26-
messages,
27-
model: AzureOpenAIConfig.deployment,
28-
max_tokens: 1000,
29-
temperature: 0.7,
30-
};
31-
32-
const response = await chatService.chat.completions.create(params);
33-
const data = { choices: response.choices };
34-
35-
if (delay) {
36-
await wait(delay);
37-
}
38-
39-
return data.choices[0].message?.content ?? '';
40-
}
9+
import { getAIResponse } from './service.ts';
10+
import type { AIMessage } from './service.ts';
4111

4212
const store: ChatTypes.Message[] = [];
4313

@@ -61,12 +31,12 @@ export const dataSource = new DataSource({
6131
paginate: false,
6232
});
6333

64-
const dataItemToMessage = (item: ChatTypes.Message): Message => ({
65-
role: item.author?.id as Message['role'],
34+
const dataItemToMessage = (item: ChatTypes.Message): AIMessage => ({
35+
role: item.author?.id as AIMessage['role'],
6636
content: item.text,
6737
});
6838

69-
const getMessageHistory = (): Message[] => [...dataSource.items()].map(dataItemToMessage);
39+
const getMessageHistory = (): AIMessage[] => [...dataSource.items()].map(dataItemToMessage);
7040

7141
export const useApi = () => {
7242
const [alerts, setAlerts] = useState<ChatTypes.Alert[]>([]);

apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/data.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
export const AzureOpenAIConfig = {
2-
dangerouslyAllowBrowser: true,
3-
deployment: 'gpt-4o-mini',
4-
apiVersion: '2024-02-01',
5-
endpoint: 'https://public-api.devexpress.com/demo-openai',
6-
apiKey: 'DEMO',
7-
};
81
export const REGENERATION_TEXT = 'Regeneration...';
92
export const CHAT_DISABLED_CLASS = 'chat-disabled';
103
export const ALERT_TIMEOUT = 1000 * 60;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { AzureOpenAI } from 'openai';
2+
3+
const AzureOpenAIConfig = {
4+
dangerouslyAllowBrowser: true,
5+
deployment: 'gpt-4o-mini',
6+
apiVersion: '2024-02-01',
7+
endpoint: 'https://public-api.devexpress.com/demo-openai',
8+
apiKey: 'DEMO',
9+
};
10+
const chatService = new AzureOpenAI(AzureOpenAIConfig);
11+
const wait = (delay) =>
12+
new Promise((resolve) => {
13+
setTimeout(resolve, delay);
14+
});
15+
export async function getAIResponse(messages, delay) {
16+
const params = {
17+
messages,
18+
model: AzureOpenAIConfig.deployment,
19+
max_tokens: 1000,
20+
temperature: 0.7,
21+
};
22+
const response = await chatService.chat.completions.create(params);
23+
const data = { choices: response.choices };
24+
if (delay) {
25+
await wait(delay);
26+
}
27+
return data.choices[0].message?.content ?? '';
28+
}

apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/useApi.js

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,8 @@
11
import { useCallback, useState } from 'react';
2-
import { AzureOpenAI } from 'openai';
32
import { CustomStore, DataSource } from 'devextreme-react/common/data';
4-
import {
5-
ALERT_TIMEOUT, assistant, AzureOpenAIConfig, REGENERATION_TEXT,
6-
} from './data.js';
3+
import { ALERT_TIMEOUT, assistant, REGENERATION_TEXT } from './data.js';
4+
import { getAIResponse } from './service.js';
75

8-
const chatService = new AzureOpenAI(AzureOpenAIConfig);
9-
const wait = (delay) =>
10-
new Promise((resolve) => {
11-
setTimeout(resolve, delay);
12-
});
13-
export async function getAIResponse(messages, delay) {
14-
const params = {
15-
messages,
16-
model: AzureOpenAIConfig.deployment,
17-
max_tokens: 1000,
18-
temperature: 0.7,
19-
};
20-
const response = await chatService.chat.completions.create(params);
21-
const data = { choices: response.choices };
22-
if (delay) {
23-
await wait(delay);
24-
}
25-
return data.choices[0].message?.content ?? '';
26-
}
276
const store = [];
287
const customStore = new CustomStore({
298
key: 'id',

apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949

5050
<script setup lang="ts">
5151
import { ref, onBeforeMount } from 'vue';
52-
import { AzureOpenAI } from 'openai';
5352
import DxChat, { type DxChatTypes } from 'devextreme-vue/chat';
5453
import DxButton from 'devextreme-vue/button';
5554
import { loadMessages } from 'devextreme-vue/common/core/localization';
@@ -62,12 +61,10 @@ import {
6261
assistant,
6362
dataSource,
6463
convertToHtml,
65-
AzureOpenAIConfig,
6664
REGENERATION_TEXT,
6765
ALERT_TIMEOUT,
6866
} from './data.ts';
69-
70-
const chatService = new AzureOpenAI(AzureOpenAIConfig);
67+
import { getAIResponse } from './service.ts';
7168
7269
const typingUsers = ref<{ id: string, name: string }[]>([]);
7370
const alerts = ref<{ message: string }[]>([]);
@@ -78,20 +75,6 @@ onBeforeMount(() => {
7875
loadMessages(dictionary);
7976
});
8077
81-
async function getAIResponse(messages: DxChatTypes.Message[]): Promise<string> {
82-
const params: Record<string, any> = {
83-
messages,
84-
model: AzureOpenAIConfig.deployment,
85-
max_tokens: 1000,
86-
temperature: 0.7,
87-
};
88-
89-
const response = await chatService.chat.completions.create(params as any);
90-
const data = { choices: response.choices };
91-
92-
return data.choices[0].message?.content || '';
93-
}
94-
9578
function toggleDisabledState(disabled: boolean, event?: Events.EventObject): void {
9679
isDisabled.value = disabled;
9780
@@ -130,11 +113,11 @@ async function processMessageSending(
130113
): Promise<void> {
131114
toggleDisabledState(true, event);
132115
133-
messages.push({ role: 'user', content: message.text });
116+
messages.push({ role: 'user', content: message.text ?? '' });
134117
typingUsers.value = [assistant];
135118
136119
try {
137-
const aiResponse = await getAIResponse(messages);
120+
const aiResponse = await getAIResponse(messages) as string;
138121
139122
setTimeout(() => {
140123
typingUsers.value = [];
@@ -165,7 +148,7 @@ async function regenerate(): Promise<void> {
165148
const lastMessage = messages.at(-1);
166149
167150
try {
168-
const aiResponse = await getAIResponse(messages.slice(0, -1));
151+
const aiResponse = await getAIResponse(messages.slice(0, -1)) as string;
169152
170153
updateLastMessage(aiResponse);
171154

0 commit comments

Comments
 (0)