JavaScript/jQuery Chat - Integrate with AI Service

You can integrate DevExtreme Chat with various AI services, such as OpenAI, Azure OpenAI, Google Dialogflow, and Microsoft Bot Framework.

NOTE

Review this demo for Azure OpenAI integration:

View Demo

OpenAI

This help topic describes how to integrate Chat with OpenAI. You can find the full example code in the following GitHub repository:

View on GitHub

Prerequisites and Installation

First, obtain an API key. Next, install OpenAI SDK:

jQuery
index.html
<head>
    <!-- ... -->
    <script type="module">
        import OpenAI from "https://esm.sh/openai@4.73.1";
    </script>
</hea>
NOTE
Ensure DevExtreme is installed in your project.
Angular
npm install openai 
NOTE
Ensure DevExtreme is installed in your project.
Vue
npm install openai
NOTE
Ensure DevExtreme is installed in your project.
React
npm install openai
NOTE
Ensure DevExtreme is installed in your project.

AI Configuration

Create an instance of OpenAI:

jQuery
const chatService = new OpenAI({ 
    dangerouslyAllowBrowser: true, 
    apiKey: "OPENAI_API_KEY", // insert your OpenAI API key
}); 
Angular
TypeScript
import { OpenAI } from "openai";

export class AppService {
    chatService: OpenAI;
    OpenAIConfig = {
        dangerouslyAllowBrowser: true,
        apiKey: "OPENAI_API_KEY", // insert your OpenAI API key
    };
    constructor() {
        this.chatService = new OpenAI(this.OpenAIConfig);
    }
}
Vue
TypeScript
import { OpenAI } from 'openai';

const OpenAIConfig = {
    dangerouslyAllowBrowser: true,
    apiKey: 'OPEN_AI_KEY', // insert your OpenAI API key
};

const chatService = new OpenAI(OpenAIConfig);
React
TypeScript
import { OpenAI } from "openai";

class AppService {
    chatService: OpenAI;
    OpenAIConfig = {
        dangerouslyAllowBrowser: true,
        apiKey: "OPENAI_API_KEY", // insert your OpenAI API key
    };
    constructor() {
        this.chatService = new OpenAI(this.OpenAIConfig);
    }
}
IMPORTANT
dangerouslyAllowBrowser: true enables browser-side requests. This exposes the API key. For production, route requests through your backend.

Implement a getAIResponse(messages) function to call the OpenAI API for responses. Here, the incoming argument messages is an array of { role: "user" | "assistant" | "system"; content: string } objects.

jQuery
async function getAIResponse(messages) {
    const params = {
        messages,
        model: 'gpt-4o-mini',
    };
    const response = await chatService.chat.completions.create(params);
    return response.choices[0].message?.content;
}
Angular
TypeScript
async getAIResponse(messages: Array<{ role: "user" | "assistant" | "system"; content: string }>) {
    const params = {
    messages: messages.map(msg => ({
        role: msg.role,
        content: msg.content
    })),
    model: 'gpt-4o-mini',
    };
    const response = await this.chatService.chat.completions.create(params);
    const data = { choices: response.choices };
    return data.choices[0].message?.content;
}
Vue
TypeScript
const getAIResponse = async(messages: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>) => {
    const params = {
    messages: messages.map(msg => ({
        role: msg.role,
        content: msg.content
    })),
    model: 'gpt-4o-mini'
    };

    const response = await chatService.chat.completions.create(params);
    return response.choices[0].message?.content;
};
React
TypeScript
async getAIResponse(messages: { role: 'user' | 'assistant' | 'system'; content: string }[]): Promise<any> {
    const params = {
    messages: messages.map((msg) => ({
        role: msg.role,
        content: msg.content,
    })),
    model: 'gpt-4o-mini',
    };

    const response = await this.chatService.chat.completions.create(params);
    const data = { choices: response.choices };
    return data.choices[0].message?.content;
}

Chat Configuration

To synchronize Chat and OpenAI, declare the processMessageSending() function. This function configures typingUsers, pushes the assistant message to the store, and renders the message.

jQuery
index.js
async function processMessageSending() {
    toggleDisabledState(true);
    instance.option({ typingUsers: [assistant] });
    try {
        const aiResponse = await getAIResponse(messages);
        setTimeout(() => {
            instance.option({ typingUsers: [] });
            messages.push({ role: 'assistant', content: aiResponse });
            renderMessage(aiResponse);
        }, 200);
    } catch {
        instance.option({ typingUsers: [] });
        alertLimitReached();
    } finally {
        toggleDisabledState(false);
    }
}
Angular
TypeScript
async processMessageSending() {
    this.toggleDisabledState(true);
    this.typingUsersSubject.next([this.assistant]);
    try {
        const aiResponse = await this.getAIResponse(this.messages);
        setTimeout(() => {
            this.typingUsersSubject.next([]);
            this.messages.push({ role: "assistant", content: aiResponse ?? "" });
            this.renderAssistantMessage(aiResponse ?? "");
        }, 200);
    } catch {
        this.typingUsersSubject.next([]);
        this.alertLimitReached();
    } finally {
        this.toggleDisabledState(false);
    }
}
Vue
TypeScript
const processMessageSending = async() => {
    toggleDisabledState(true);
    typingUsers.value = [assistant];
    try {
        const aiResponse = await getAIResponse(messages.value);
        setTimeout(() => {
            typingUsers.value = [];
            messages.value.push({ role: 'assistant', content: aiResponse ?? '' });
            renderAssistantMessage(aiResponse ?? '');
        }, 200);
    } catch {
        typingUsers.value = [];
        alertLimitReached();
    } finally {
        toggleDisabledState(false);
    }
};
React
TypeScript
 async processMessageSending(): Promise<void> {
    this.toggleDisabledState(true);
    this.typingUsersSubject.next([this.assistant]);
    try {
        const aiResponse = await this.getAIResponse(this.messages);
        setTimeout(() => {
            this.typingUsersSubject.next([]);
            this.messages.push({ role: 'assistant', content: aiResponse ?? '' });
            this.renderAssistantMessage(aiResponse ?? '');
        }, 200);
    } catch {
        this.typingUsersSubject.next([]);
        this.alertLimitReached();
    } finally {
        this.toggleDisabledState(false);
    }
}

Call processMessageSending() after a user message is sent in the onMessageEntered event handler:

jQuery
index.js
const instance = $('#dx-ai-chat').dxChat({
    // ...
    dataSource: customStore,
    reloadOnChange: false,
    onMessageEntered: (e) => {
        const { message } = e;
        customStore.push([{ type: 'insert', data: { id: Date.now(), ...message } }]);
        messages.push({ role: 'user', content: message.text });
        processMessageSending();
    }
}).dxChat('instance');
Angular
TypeScript
 async onMessageEntered({ message, event }: MessageEnteredEvent) {
    this.dataSource
    ?.store()
    .push([{ type: "insert", data: { id: Date.now(), ...message } }]);

    this.messages.push({ role: "user", content: message?.text ?? "" });
    this.processMessageSending();
}
Vue
TypeScript
const onMessageEntered = async(e: MessageEnteredEvent) => {
    let { message } = e;
    dataSource.value?.store().push([{
        type: 'insert',
        data: { id: Date.now(), ...message }
    }]);

    messages.value.push({ role: 'user', content: message?.text ?? '' });
    await processMessageSending();
};
React
TypeScript
onMessageEntered({ message }: MessageEnteredEvent): void {
    this.dataSource
    ?.store()
    .push([{ type: 'insert', data: { id: Date.now(), ...message } }]);

    this.messages.push({ role: 'user', content: message?.text ?? '' });
    void this.processMessageSending();
}

You can also implement additional UI capabilities to further improve user experience:

  • Add a Markdown converter for assistant outputs. For more information, refer to the Markdown Support help topic.
  • Define a messageTemplate for the assistant’s responses and add two buttons: copy and regenerate response. See the example code in the GitHub repository:

View on GitHub