Skip to content

Commit

Permalink
Chat AI Demo: Add model to params, fix import in Vue demo
Browse files Browse the repository at this point in the history
Co-authored-by: marker dao ® <[email protected]>
  • Loading branch information
Zedwag and marker dao ® authored Dec 11, 2024
1 parent f268b71 commit a5a491c
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class AppService {
async getAIResponse(messages) {
const params = {
messages,
model: this.AzureOpenAIConfig.deployment,
max_tokens: 1000,
temperature: 0.7,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const chatService = new AzureOpenAI(AzureOpenAIConfig);
async function getAIResponse(messages) {
const params = {
messages,
model: AzureOpenAIConfig.deployment,
max_tokens: 1000,
temperature: 0.7,
};
Expand Down
80 changes: 27 additions & 53 deletions apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,122 +4,103 @@ import { AzureOpenAI } from 'openai';
import CustomStore from 'devextreme/data/custom_store';
import DataSource from 'devextreme/data/data_source';
import { loadMessages } from 'devextreme/localization';
import {
import {
user,
assistant,
AzureOpenAIConfig,
REGENERATION_TEXT,
CHAT_DISABLED_CLASS,
ALERT_TIMEOUT
ALERT_TIMEOUT,
} from './data.js';
import Message from './Message.js';

const store = [];
const messages = [];

loadMessages({
en: {
'dxChat-emptyListMessage': 'Chat is Empty',
'dxChat-emptyListPrompt': 'AI Assistant is ready to answer your questions.',
'dxChat-textareaPlaceholder': 'Ask AI Assistant...',
},
});

const chatService = new AzureOpenAI(AzureOpenAIConfig);

async function getAIResponse(messages) {
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 };

return data.choices[0].message?.content;
}

function updateLastMessage(text = REGENERATION_TEXT) {
const items = dataSource.items();
const lastMessage = items.at(-1);

dataSource.store().push([{
type: 'update',
key: lastMessage.id,
data: { text },
}]);
dataSource.store().push([
{
type: 'update',
key: lastMessage.id,
data: { text },
},
]);
}

function renderAssistantMessage(text) {
const message = {
id: Date.now(),
timestamp: new Date(),
author: assistant,
text,
};

dataSource.store().push([{ type: 'insert', data: message }]);
}

const customStore = new CustomStore({
key: 'id',
load: () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([...store]);
}, 0);
});
},
insert: (message) => {
return new Promise((resolve) => {
setTimeout(() => {
store.push(message);
resolve(message);
});
load: () => new Promise((resolve) => {
setTimeout(() => {
resolve([...store]);
}, 0);
}),
insert: (message) => new Promise((resolve) => {
setTimeout(() => {
store.push(message);
resolve(message);
});
},
}),
});

const dataSource = new DataSource({
store: customStore,
paginate: false,
})

});
export default function App() {
const [alerts, setAlerts] = useState([]);
const [typingUsers, setTypingUsers] = useState([]);
const [classList, setClassList] = useState('');

function alertLimitReached() {
setAlerts([{
message: 'Request limit reached, try again in a minute.'
}]);

setAlerts([
{
message: 'Request limit reached, try again in a minute.',
},
]);
setTimeout(() => {
setAlerts([]);
}, ALERT_TIMEOUT);
}

function toggleDisabledState(disabled, event = undefined) {
setClassList(disabled ? CHAT_DISABLED_CLASS : '');

if (disabled) {
event?.target.blur();
} else {
event?.target.focus();
}
};

}
async function processMessageSending(message, event) {
toggleDisabledState(true, event);

messages.push({ role: 'user', content: message.text });
setTypingUsers([assistant]);

try {
const aiResponse = await getAIResponse(messages);

setTimeout(() => {
setTypingUsers([]);
messages.push({ role: 'assistant', content: aiResponse });
Expand All @@ -133,13 +114,10 @@ export default function App() {
toggleDisabledState(false, event);
}
}

async function regenerate() {
toggleDisabledState(true);

try {
const aiResponse = await getAIResponse(messages.slice(0, -1));

updateLastMessage(aiResponse);
messages.at(-1).content = aiResponse;
} catch {
Expand All @@ -149,20 +127,16 @@ export default function App() {
toggleDisabledState(false);
}
}

function onMessageEntered({ message, event }) {
dataSource.store().push([{ type: 'insert', data: { id: Date.now(), ...message } }]);

if (!alerts.length) {
processMessageSending(message, event);
}
}

function onRegenerateButtonClick() {
updateLastMessage();
regenerate();
}

return (
<Chat
className={classList}
Expand Down
88 changes: 39 additions & 49 deletions apps/demos/Demos/Chat/AIAndChatbotIntegration/ReactJs/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,49 @@ import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import HTMLReactParser from 'html-react-parser';

import { REGENERATION_TEXT } from './data.js';

function convertToHtml(value) {
const result = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.processSync(value)
.toString();

return result;
const result = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.processSync(value)
.toString();
return result;
}

function Message({ message }, onRegenerateButtonClick) {
const [icon, setIcon] = useState('copy');

if (message.text === REGENERATION_TEXT) {
return <span>{REGENERATION_TEXT}</span>;
}

function onCopyButtonClick() {
navigator.clipboard?.writeText(message.text);
setIcon('check');

setTimeout(() => {
setIcon('copy');
}, 2500);
}

return (
<React.Fragment>
<div
className='dx-chat-messagebubble-text'
>
{HTMLReactParser(convertToHtml(message.text))}
</div>
<div className='dx-bubble-button-container'>
<Button
icon={icon}
stylingMode='text'
hint='Copy'
onClick={onCopyButtonClick}
/>
<Button
icon='refresh'
stylingMode='text'
hint='Regenerate'
onClick={onRegenerateButtonClick}
/>
</div>
</React.Fragment>
)
const [icon, setIcon] = useState('copy');
if (message.text === REGENERATION_TEXT) {
return <span>{REGENERATION_TEXT}</span>;
}
function onCopyButtonClick() {
navigator.clipboard?.writeText(message.text);
setIcon('check');
setTimeout(() => {
setIcon('copy');
}, 2500);
}
return (
<React.Fragment>
<div className="dx-chat-messagebubble-text">
{HTMLReactParser(convertToHtml(message.text))}
</div>
<div className="dx-bubble-button-container">
<Button
icon={icon}
stylingMode="text"
hint="Copy"
onClick={onCopyButtonClick}
/>
<Button
icon="refresh"
stylingMode="text"
hint="Regenerate"
onClick={onRegenerateButtonClick}
/>
</div>
</React.Fragment>
);
}

export default Message;
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ export const AzureOpenAIConfig = {
apiVersion: '2024-02-01',
endpoint: 'https://public-api.devexpress.com/demo-openai',
apiKey: 'DEMO',
}

};
export const REGENERATION_TEXT = 'Regeneration...';
export const CHAT_DISABLED_CLASS = 'dx-chat-disabled';
export const ALERT_TIMEOUT = 1000 * 60;

export const user = {
id: 'user',
};

export const assistant = {
id: 'assistant',
name: 'Virtual Assistant',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.js';

ReactDOM.render(
<App />,
document.getElementById('app'),
);
ReactDOM.render(<App />, document.getElementById('app'));
8 changes: 4 additions & 4 deletions apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,9 @@ import { ref, onBeforeMount } from 'vue';
import DxChat from 'devextreme-vue/chat';
import DxButton from 'devextreme-vue/button';
import { loadMessages } from 'devextreme/localization';
import { AzureOpenAI } from 'openai';
import openai from 'openai';
import {
dictionary,
store,
messages,
user,
assistant,
Expand All @@ -67,7 +66,7 @@ import {
ALERT_TIMEOUT,
} from './data.ts';
const chatService = new AzureOpenAI(AzureOpenAIConfig);
const chatService = new openai.AzureOpenAI(AzureOpenAIConfig);
const typingUsers = ref([]);
const alerts = ref([]);
Expand All @@ -81,6 +80,7 @@ onBeforeMount(() => {
async function getAIResponse(messages) {
const params = {
messages,
model: AzureOpenAIConfig.deployment,
max_tokens: 1000,
temperature: 0.7,
};
Expand All @@ -91,7 +91,7 @@ async function getAIResponse(messages) {
return data.choices[0].message?.content;
}
function toggleDisabledState(disabled, event) {
function toggleDisabledState(disabled, event = undefined) {
isDisabled.value = disabled;
if (disabled) {
Expand Down
3 changes: 1 addition & 2 deletions apps/demos/Demos/Chat/AIAndChatbotIntegration/Vue/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ export const CHAT_DISABLED_CLASS = 'dx-chat-disabled';
export const ALERT_TIMEOUT = 1000 * 60;

export const user = {
id: 'c94c0e76-fb49-4b9b-8f07-9f93ed93b4f3',
name: 'John Doe',
id: 'user',
};

export const assistant = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ $(() => {
async function getAIResponse(messages) {
const params = {
messages,
model: deployment,
max_tokens: 1000,
temperature: 0.7,
};
Expand Down

0 comments on commit a5a491c

Please sign in to comment.