Skip to content

Commit

Permalink
feat: migrate use chatbot mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
ReidyT authored Dec 11, 2023
2 parents d8496c0 + a9866f0 commit fd76540
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 66 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
REACT_APP_GRAASP_APP_KEY: id-1234567890
REACT_APP_ENABLE_MOCK_API: true
with:
wait-on: http://localhost:3000
wait-on-timeout: 180
install: false
build: yarn build
config: baseUrl=http://localhost:3000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ jobs:
REACT_APP_API_HOST: ${{ secrets.REACT_APP_API_HOST_DEV }}
REACT_APP_GRAASP_APP_KEY: ${{ secrets.APP_KEY }}
REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
REACT_APP_OPEN_AI_URL: ${{ secrets.REACT_APP_OPEN_AI_URL_DEV }}
run: yarn build
shell: bash

Expand All @@ -41,6 +40,6 @@ jobs:
version: 'latest'
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_DEV }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEV }}
aws-region: ${{ secrets.APPS_AWS_REGION }}
aws-s3-bucket-name: ${{ secrets.AWS_S3_BUCKET_NAME_APPS_DEV }}
aws-region: ${{ vars.APPS_AWS_REGION_DEV }}
aws-s3-bucket-name: ${{ vars.AWS_S3_BUCKET_NAME_APPS_DEV }}
cloudfront-distribution-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_APPS_DEV }}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ jobs:
REACT_APP_API_HOST: ${{ secrets.REACT_APP_API_HOST_PROD }}
REACT_APP_GRAASP_APP_KEY: ${{ secrets.APP_KEY }}
REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
REACT_APP_OPEN_AI_URL: ${{ secrets.REACT_APP_OPEN_AI_URL_PROD }}
run: yarn build
shell: bash

Expand All @@ -40,6 +39,6 @@ jobs:
version: 'latest'
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_PROD }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }}
aws-region: ${{ secrets.APPS_AWS_REGION }}
aws-s3-bucket-name: ${{ secrets.AWS_S3_BUCKET_NAME_APPS_PROD }}
aws-region: ${{ vars.APPS_AWS_REGION_PROD }}
aws-s3-bucket-name: ${{ vars.AWS_S3_BUCKET_NAME_APPS_PROD }}
cloudfront-distribution-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_APPS_PROD }}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ jobs:
REACT_APP_API_HOST: ${{ secrets.REACT_APP_API_HOST_STAGE }}
REACT_APP_GRAASP_APP_KEY: ${{ secrets.APP_KEY }}
REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
REACT_APP_OPEN_AI_URL: ${{ secrets.REACT_APP_OPEN_AI_URL_STAGE }}
run: yarn build
shell: bash

Expand All @@ -40,6 +39,6 @@ jobs:
version: 'latest'
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_STAGE }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_STAGE }}
aws-region: ${{ secrets.APPS_AWS_REGION }}
aws-s3-bucket-name: ${{ secrets.AWS_S3_BUCKET_NAME_APPS_STAGE }}
aws-region: ${{ vars.APPS_AWS_REGION_STAGE }}
aws-s3-bucket-name: ${{ vars.AWS_S3_BUCKET_NAME_APPS_STAGE }}
cloudfront-distribution-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_APPS_STAGE }}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
REACT_APP_ENABLE_MOCK_API=true
REACT_APP_GRAASP_DOMAIN=localhost
REACT_APP_API_HOST=<request address for the backend>
REACT_APP_OPEN_AI_URL=<url to make request>
```

![GitHub package.json version](https://img.shields.io/github/package-json/v/graasp/graasp-app-text-analysis?color=green&style=flat-square)

<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-)

<!-- ALL-CONTRIBUTORS-BADGE:END -->

## Contributors ✨
Expand Down
73 changes: 33 additions & 40 deletions src/components/common/chat/ChatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ import {
import { DEFAULT_INITIAL_PROMPT } from '../../../config/appSettings';
import {
ANONYMOUS_USER,
CHATBOT_PREFIX,
CHATBOT_RESPONSE_URL,
MAX_CONVERSATION_LENGTH,
MAX_CONVERSATION_LENGTH_ALERT,
SCROLL_SAFETY_MARGIN,
STUDENT_PREFIX,
} from '../../../config/constants';
import { CHAT_BOT_ERROR_MESSAGE } from '../../../config/messages';
import { mutations } from '../../../config/queryClient';
import { CHATBOT_MODE_CY, messagesDataCy } from '../../../config/selectors';
import {
DEFAULT_BORDER_RADIUS,
LIGHT_GRAY,
LIGHT_VIOLET,
} from '../../../config/stylingConstants';
import { ThreadMessage } from '../../../interfaces/chatbot';
import { buildPrompt } from '../../../utils/chatbot';
import { useAppDataContext } from '../../context/AppDataContext';
import { useAppSettingContext } from '../../context/AppSettingContext';
import { useMembersContext } from '../../context/MembersContext';
Expand Down Expand Up @@ -67,6 +68,7 @@ const ChatBox: FC<Prop> = ({ focusWord, isOpen }) => {
const memberName = member?.name || ANONYMOUS_USER;
const initial = memberName.toLocaleUpperCase().trim()[0];
const [loading, setLoading] = useState(false);
const { mutateAsync: postChatBot } = mutations.usePostChatBot();

const initialPrompt = (
(appSettingArray.find((s) => s.name === INITIAL_PROMPT_SETTING_KEY)?.data ||
Expand All @@ -82,49 +84,40 @@ const ChatBox: FC<Prop> = ({ focusWord, isOpen }) => {
)
.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1)) as ChatAppData[];

const fetchApi = async (input: string): Promise<{ completion: string }> => {
const chatConcatMessages = [
initialPrompt,
...chatAppData.map((data) => {
const prefix =
data.type === APP_DATA_TYPES.BOT_COMMENT
? CHATBOT_PREFIX
: STUDENT_PREFIX;
return `${prefix}: ${data.data.message}`;
}),
`${STUDENT_PREFIX}: ${input}`,
].join('\n\n\n\n');

setLoading(true);

const response = await fetch(CHATBOT_RESPONSE_URL, {
method: 'POST',
body: JSON.stringify({ prompt: chatConcatMessages }),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
});
const json = await response.json();
return json;
};

const onSend = (input: string): void => {
if (input.trim() !== '') {
postAppDataAsync({
data: { message: input, keyword: focusWord },
type: APP_DATA_TYPES.STUDENT_COMMENT,
})
?.then(() => fetchApi(input))
.then((json) => {
setLoading(false);
postAppData({
data: {
message: json.completion.replace(`${CHATBOT_PREFIX}:`, '').trim(),
keyword: focusWord,
},
type: APP_DATA_TYPES.BOT_COMMENT,
})?.then(() => {
const thread: ThreadMessage[] = chatAppData.map((data) => ({
type: data.type as
| APP_DATA_TYPES.BOT_COMMENT
| APP_DATA_TYPES.STUDENT_COMMENT,
data: { content: data.data.message },
}));

const prompt = buildPrompt(initialPrompt, thread, input);

setLoading(true);

const appData = {
message: CHAT_BOT_ERROR_MESSAGE,
keyword: focusWord,
};

postChatBot(prompt)
.then((chatBotRes) => {
appData.message = chatBotRes.completion;
})
.finally(() => {
setLoading(false);
postAppData({
data: appData,
type: APP_DATA_TYPES.BOT_COMMENT,
});
});
});
});
}
};

Expand Down
5 changes: 0 additions & 5 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { TextResourceData } from './appSettingTypes';

export const ANONYMOUS_USER = 'Anonymous';
export const ENTER_KEY = 'Enter';
export const { REACT_APP_OPEN_AI_URL: CHATBOT_RESPONSE_URL = 'url not found' } =
process.env;
export const FIRST_CHATBOT_MESSAGE: TextResourceData = {
text: 'You clicked on {{keyword}}, what do you want to know about it ?',
};
Expand All @@ -14,9 +12,6 @@ export const MAX_CONVERSATION_LENGTH = 20;
export const MAX_CONVERSATION_LENGTH_ALERT =
'You have reached the maximum number of messages allowed in the conversation';

export const STUDENT_PREFIX = 'Student';
export const CHATBOT_PREFIX = 'Chatbot';

export const SCROLL_SAFETY_MARGIN = 64;

export const DICTIONARY_API_BASE_URL =
Expand Down
2 changes: 2 additions & 0 deletions src/config/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export const CONTEXT_FETCHING_ERROR_MESSAGE =
'An error occurred while fetching the context.';
export const TOKEN_REQUEST_ERROR_MESSAGE =
'An error occurred while requesting the token.';
export const CHAT_BOT_ERROR_MESSAGE =
'Sorry, an error occurred with the chatbot.';
5 changes: 1 addition & 4 deletions src/data/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ export const mockContext: LocalContext = {

export const mockMembers = Object.values(MEMBERS);

const buildDatabase = (
appContext: Partial<LocalContext>,
members?: CompleteMember[],
): Database => ({
const buildDatabase = (members: CompleteMember[]): Database => ({
appData: [],
appActions: [],
members: members ?? Object.values(MEMBERS),
Expand Down
11 changes: 4 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import { MockSolution, mockApi } from '@graasp/apps-query-client';
import * as Sentry from '@sentry/react';

import Root from './components/Root';
import {
CHATBOT_RESPONSE_URL,
DICTIONARY_API_BASE_URL,
} from './config/constants';
import { DICTIONARY_API_BASE_URL } from './config/constants';
import { MOCK_API } from './config/env';
import { generateSentryConfig } from './config/sentry';
import buildDatabase, { mockContext } from './data/db';
import buildDatabase, { mockContext, mockMembers } from './data/db';
import './index.css';

Sentry.init({
Expand All @@ -29,9 +26,9 @@ Sentry.init({
if (MOCK_API) {
mockApi(
{
externalUrls: [`${DICTIONARY_API_BASE_URL}**`, CHATBOT_RESPONSE_URL],
externalUrls: [`${DICTIONARY_API_BASE_URL}**`],
appContext: window.Cypress ? window.appContext : mockContext,
database: window.Cypress ? window.database : buildDatabase(mockContext),
database: window.Cypress ? window.database : buildDatabase(mockMembers),
},
window.Cypress ? MockSolution.MirageJS : MockSolution.ServiceWorker,
);
Expand Down
9 changes: 9 additions & 0 deletions src/interfaces/chatbot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { APP_DATA_TYPES } from '../config/appDataTypes';

// TODO: there is something similar in app-code-capsule, it may be intersting to move in SDK ?
export interface ThreadMessage {
type: APP_DATA_TYPES.BOT_COMMENT | APP_DATA_TYPES.STUDENT_COMMENT;
data: {
content: string;
};
}
30 changes: 30 additions & 0 deletions src/utils/chatbot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ChatBotMessage, ChatbotRole } from '@graasp/sdk';

import { APP_DATA_TYPES } from '../config/appDataTypes';
import { ThreadMessage } from '../interfaces/chatbot';

// TODO: maybe it is possible to move to SDK and to reuse it in code-capsule
export const buildPrompt = (
initialPrompt: string | undefined,
threadMessages: ThreadMessage[],
userMessage: string,
): Array<ChatBotMessage> => {
// define the message to send to OpenAI with the initial prompt first if needed (role system).
// Each call to OpenAI must contain the whole history of the messages.
const finalPrompt: Array<ChatBotMessage> = initialPrompt
? [{ role: ChatbotRole.System, content: initialPrompt }]
: [];

threadMessages.forEach((msg) => {
const msgRole =
msg.type === APP_DATA_TYPES.BOT_COMMENT
? ChatbotRole.Assistant
: ChatbotRole.User;
finalPrompt.push({ role: msgRole, content: msg.data.content });
});

// add the last user's message in the prompt
finalPrompt.push({ role: ChatbotRole.User, content: userMessage });

return finalPrompt;
};

0 comments on commit fd76540

Please sign in to comment.