From c3a653bbc8adf6f68aa2fa7453818484378697ed Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Fri, 11 Oct 2024 16:10:31 +0530 Subject: [PATCH 01/18] added assistants page --- src/assets/images/Assistants.svg | 9 ++ src/assets/images/FileGreen.svg | 8 + .../images/icons/SideDrawer/Assistant.tsx | 12 ++ src/common/HelpData.tsx | 8 +- .../UI/Form/AutoComplete/AutoComplete.tsx | 4 + src/components/UI/Heading/Heading.module.css | 6 + src/components/UI/Heading/Heading.tsx | 35 ++++- src/components/UI/HelpIcon/HelpIcon.tsx | 18 ++- .../Navigation/SideMenus/SideMenus.module.css | 8 + .../Layout/Navigation/SideMenus/SideMenus.tsx | 1 + src/components/UI/ListIcon/ListIcon.tsx | 2 + src/config/menu.ts | 9 ++ .../AssistantOptions.module.css | 40 +++++ .../AssistantOptions/AssistantOptions.tsx | 91 ++++++++++++ .../Assistants/Assistants.module.css | 39 +++++ src/containers/Assistants/Assistants.tsx | 60 ++++++++ .../CreateAssistant.module.css | 34 +++++ .../CreateAssistant/CreateAssistant.tsx | 132 +++++++++++++++++ .../Assistants/ListItems/List.module.css | 93 ++++++++++++ src/containers/Assistants/ListItems/List.tsx | 139 ++++++++++++++++++ src/graphql/mutations/Assistant.ts | 12 ++ src/graphql/queries/Assistant.ts | 35 +++++ .../AuthenticatedRoute/AuthenticatedRoute.tsx | 3 + 23 files changed, 787 insertions(+), 11 deletions(-) create mode 100644 src/assets/images/Assistants.svg create mode 100644 src/assets/images/FileGreen.svg create mode 100644 src/assets/images/icons/SideDrawer/Assistant.tsx create mode 100644 src/containers/Assistants/AssistantOptions/AssistantOptions.module.css create mode 100644 src/containers/Assistants/AssistantOptions/AssistantOptions.tsx create mode 100644 src/containers/Assistants/Assistants.module.css create mode 100644 src/containers/Assistants/Assistants.tsx create mode 100644 src/containers/Assistants/CreateAssistant/CreateAssistant.module.css create mode 100644 src/containers/Assistants/CreateAssistant/CreateAssistant.tsx create mode 100644 src/containers/Assistants/ListItems/List.module.css create mode 100644 src/containers/Assistants/ListItems/List.tsx create mode 100644 src/graphql/mutations/Assistant.ts create mode 100644 src/graphql/queries/Assistant.ts diff --git a/src/assets/images/Assistants.svg b/src/assets/images/Assistants.svg new file mode 100644 index 000000000..5377e7758 --- /dev/null +++ b/src/assets/images/Assistants.svg @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/images/FileGreen.svg b/src/assets/images/FileGreen.svg new file mode 100644 index 000000000..ea28a4e2f --- /dev/null +++ b/src/assets/images/FileGreen.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/icons/SideDrawer/Assistant.tsx b/src/assets/images/icons/SideDrawer/Assistant.tsx new file mode 100644 index 000000000..dc088f50b --- /dev/null +++ b/src/assets/images/icons/SideDrawer/Assistant.tsx @@ -0,0 +1,12 @@ +const SvgComponent = ({ color }: { color: string }) => ( + + + + + + +); +export default SvgComponent; diff --git a/src/common/HelpData.tsx b/src/common/HelpData.tsx index 96b25a2dd..feed84a86 100644 --- a/src/common/HelpData.tsx +++ b/src/common/HelpData.tsx @@ -1,6 +1,6 @@ export interface HelpDataProps { heading: string; - link: string; + link?: string; } export const speedSendInfo: HelpDataProps = { @@ -101,3 +101,9 @@ export const blockedContactsInfo: HelpDataProps = { 'Glific allows you to block contacts who are sending unwanted or inappropriate messages, ensuring a positive experience for both your beneficiaries and staff.', link: 'https://glific.github.io/docs/docs/Product%20Features/Others/All%20product%20features/#block-contacts', // Replace with the actual Glific documentation link }; + +export const assistantsInfo: HelpDataProps = { + heading: + 'Assistants can call OpenAI’s models with specific instructions to tune their personality and capabilities. Assistants can access multiple tools in parallel. Assistants can access files in several formats as part of their creation. When using tools, Assistants can also create files (e.g., images, spreadsheets, etc) and cite files they reference in the Messages they create.', + link: 'https://glific.github.io/docs/docs/Product%20Features/Flows/Flow%20Variables/Flow%20variables%20vs%20Contact%20variables', // Replace with the actual Glific documentation link +}; diff --git a/src/components/UI/Form/AutoComplete/AutoComplete.tsx b/src/components/UI/Form/AutoComplete/AutoComplete.tsx index ecfb76dd4..32ba5f7cb 100644 --- a/src/components/UI/Form/AutoComplete/AutoComplete.tsx +++ b/src/components/UI/Form/AutoComplete/AutoComplete.tsx @@ -91,6 +91,7 @@ export const AutoComplete = ({ const [inputValue, setInputValue] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [open, setOpen] = useState(false); + console.log(field); let classStyles = { ...classes, inputRoot: styles.DefaultInputRoot }; @@ -113,6 +114,9 @@ export const AutoComplete = ({ } return []; } + + console.log(field.value); + return field.value; })(); diff --git a/src/components/UI/Heading/Heading.module.css b/src/components/UI/Heading/Heading.module.css index 6af767b1d..e94298330 100644 --- a/src/components/UI/Heading/Heading.module.css +++ b/src/components/UI/Heading/Heading.module.css @@ -40,3 +40,9 @@ cursor: pointer; margin-top: 10px; } + +.Button { + width: 100%; + display: flex; + column-gap: 5px; +} \ No newline at end of file diff --git a/src/components/UI/Heading/Heading.tsx b/src/components/UI/Heading/Heading.tsx index fbd349871..90efed32b 100644 --- a/src/components/UI/Heading/Heading.tsx +++ b/src/components/UI/Heading/Heading.tsx @@ -2,16 +2,34 @@ import { useNavigate } from 'react-router-dom'; import HelpIcon from '../HelpIcon/HelpIcon'; import styles from './Heading.module.css'; import BackIcon from 'assets/images/icons/BackIconFlow.svg?react'; +import { Button } from '../Form/Button/Button'; +import AddIcon from 'assets/images/add.svg?react'; export interface HeadingProps { formTitle: string; helpData?: any; showHeaderHelp?: boolean; backLink?: string; + headerHelp?: string; + button?: { + show: boolean; + label: string; + action: any; + icon?: any; + }; } -export const Heading = ({ formTitle, helpData, showHeaderHelp = true, backLink }: HeadingProps) => { +export const Heading = ({ + formTitle, + helpData, + showHeaderHelp = true, + backLink, + headerHelp, + button, +}: HeadingProps) => { const navigate = useNavigate(); + const addIcon = ; + return (
@@ -24,10 +42,23 @@ export const Heading = ({ formTitle, helpData, showHeaderHelp = true, backLink } {helpData ? : ''}
- {showHeaderHelp ? `Please enter below details.` : ''} + {showHeaderHelp ? headerHelp || `Please enter below details.` : ''}
+ {button && button.show && ( +
+ +
+ )} ); }; diff --git a/src/components/UI/HelpIcon/HelpIcon.tsx b/src/components/UI/HelpIcon/HelpIcon.tsx index fb0901e6c..5648f5a1c 100644 --- a/src/components/UI/HelpIcon/HelpIcon.tsx +++ b/src/components/UI/HelpIcon/HelpIcon.tsx @@ -26,14 +26,16 @@ export const HelpIcon = ({
{helpData.heading} -
{ - window.open(helpData.link); - }} - > - Learn more -
+ {helpData.link && ( +
{ + window.open(helpData.link); + }} + > + Learn more +
+ )}
)} diff --git a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css index c8533e090..48cfcb751 100644 --- a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css +++ b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css @@ -104,3 +104,11 @@ margin-left: -10px; padding-left: 25px; } + +.New { + background-color: #119656; + font-size: 10px; + border-radius: 4px; + padding: 2px 6px; + color: #fff; +} \ No newline at end of file diff --git a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx index ed4ceb039..50a998737 100644 --- a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx +++ b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx @@ -212,6 +212,7 @@ const SideMenus = ({ opened }: SideMenusProps) => { primary={t(menu.title as any)} /> )} + {menu.new && {'New'}} ); diff --git a/src/components/UI/ListIcon/ListIcon.tsx b/src/components/UI/ListIcon/ListIcon.tsx index 7cfc3af0d..27f909353 100644 --- a/src/components/UI/ListIcon/ListIcon.tsx +++ b/src/components/UI/ListIcon/ListIcon.tsx @@ -28,6 +28,7 @@ import WaChatIcon from 'assets/images/icons/SideDrawer/WaGroupChat'; import WaCollectionIcon from 'assets/images/icons/SideDrawer/WaGroupCollection'; import WaGroupIcon from 'assets/images/icons/SideDrawer/WhatsAppGroupIcon'; import KnowledgeBaseIcon from 'assets/images/icons/SideDrawer/KnowledgeBaseIcon'; +import Assistant from 'assets/images/icons/SideDrawer/Assistant'; import styles from './ListIcon.module.css'; import FiberNewIcon from '@mui/icons-material/FiberNew'; import { Badge } from '@mui/material'; @@ -73,6 +74,7 @@ export const ListIcon = ({ icon = '', selected = false, count }: ListIconProps) waGroupChat: WaChatIcon, waGroup: WaGroupIcon, knowledgeBase: KnowledgeBaseIcon, + assistant: Assistant, }; const iconImage = stringsToIcons[icon] && ( diff --git a/src/config/menu.ts b/src/config/menu.ts index 8b1cc92e1..fb6f1a6c2 100644 --- a/src/config/menu.ts +++ b/src/config/menu.ts @@ -17,6 +17,7 @@ export interface Menu { url?: string; show?: boolean; children?: Menu[]; + new?: boolean; } // define all the menus in the system @@ -164,6 +165,14 @@ const menus = (): Menu[] => [ showBadge: true, roles: managerLevel, }, + { + title: 'Assistants', + path: '/assistants', + icon: 'assistant', + type: 'sideDrawer', + roles: allRoles, + new: true, + }, { title: 'Manage', path: '/collection', diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css b/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css new file mode 100644 index 000000000..87aab5571 --- /dev/null +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css @@ -0,0 +1,40 @@ +.AssistantOptions { + margin: 1rem 0; + display: flex; + flex-direction: column; + row-gap: 1rem; +} + +.Label { + font-size: 1rem; + font-weight: 600; + color: #555; + display: flex; + align-items: center; + column-gap: 0.5rem; +} + +.Temperature { + display: flex; + column-gap: 1rem; + align-items: center; +} + +.Slider { + width: 80%; + display: flex; + align-items: center; + column-gap: 1rem; +} + +.SliderDisplay { + font-size: 15px; + padding: 4px 12px; + border: 0.5px solid #a4a4a4; + border-radius: 4px; + width: 15%; +} + +.SliderDisplay:focus { + outline: none; +} \ No newline at end of file diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx new file mode 100644 index 000000000..99c3a4678 --- /dev/null +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -0,0 +1,91 @@ +import { FormControlLabel, Slider, Switch, Typography } from '@mui/material'; +import { useState } from 'react'; +import styles from './AssistantOptions.module.css'; +import HelpIcon from 'components/UI/HelpIcon/HelpIcon'; + +interface AssistantOptionsProps { + form: any; + field: any; + fileSearch: boolean; +} + +const temperatureInfo = + 'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.'; + +const fileSearchInfo = + 'File search enables the assistant with knowledge from files that you or your users upload. Once a file is uploaded, the assistant automatically decides when to retrieve content based on user requests.'; + +export const AssistantOptions = ({ form, field, fileSearch }: AssistantOptionsProps) => { + const [checked, setChecked] = useState(false); + + return ( +
+
+ + Tools + + { + form.setFieldValue('options', { + ...field.value, + fileSearch: event.target.checked, + }); + }} + /> + } + label={ + <> + File Search{' '} + + + } + /> + + +
+ +
+ + Temperature + + + +
+ { + form.setFieldValue('options', { + ...field.value, + temperature: value, + }); + }} + value={field.value.temperature} + step={0.01} + max={2} + /> + { + form.setFieldValue('options', { + ...field.value, + temperature: event.target.value, + }); + }} + className={styles.SliderDisplay} + /> +
+
+
+ ); +}; diff --git a/src/containers/Assistants/Assistants.module.css b/src/containers/Assistants/Assistants.module.css new file mode 100644 index 000000000..4b70a954e --- /dev/null +++ b/src/containers/Assistants/Assistants.module.css @@ -0,0 +1,39 @@ +.AssistantContainer { + width: 100%; + height: 100vh !important; +} + +.MainContainer { + width: 100%; + height: calc(100vh - 110px); + display: flex; +} + +.LeftContainer { + height: 100%; + width: 50% !important; + border-right: 1px solid #00000029; + padding: 2rem 4rem; + background-color: #f8faf5; + position: relative; +} + +.RightContainer { + height: 100%; + width: 50% !important; + overflow-y: scroll; +} + +.Search { + width: 50%; + position: absolute; + top: 0; + margin: 1rem 0; +} + +.EmptyText { + color: #555; + font-size: 1rem; + width: 100%; + text-align: center; +} \ No newline at end of file diff --git a/src/containers/Assistants/Assistants.tsx b/src/containers/Assistants/Assistants.tsx new file mode 100644 index 000000000..ce8f1f098 --- /dev/null +++ b/src/containers/Assistants/Assistants.tsx @@ -0,0 +1,60 @@ +import { assistantsInfo } from 'common/HelpData'; +import { Heading } from 'components/UI/Heading/Heading'; +import styles from './Assistants.module.css'; +import { useState } from 'react'; +import { List } from './ListItems/List'; +import { CreateAssistant } from './CreateAssistant/CreateAssistant'; +import { GET_ASSISTANTS } from 'graphql/queries/Assistant'; +import { useMutation } from '@apollo/client'; +import { CREATE_ASSISTANT } from 'graphql/mutations/Assistant'; + +export const Assistants = () => { + const [updateList, setUpdateList] = useState(false); + const [assistantId, setAssistantId] = useState(null); + const [showCreateAssistant, setShowCreateAssistant] = useState(false); + + const [createAssistant] = useMutation(CREATE_ASSISTANT); + + const handleCreateAssistant = () => { + createAssistant({ + variables: { + input: { + name: null, + }, + }, + onCompleted: () => { + setUpdateList(!updateList); + }, + }); + }; + + return ( +
+ +
+
+ +
+
+ {assistantId ? ( + + ) : ( +

Select/Create an assistant

+ )} +
+
+
+ ); +}; + +export default Assistants; diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css b/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css new file mode 100644 index 000000000..ce22aead3 --- /dev/null +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css @@ -0,0 +1,34 @@ +.FormContainer { + padding: 3rem; + height: 100%; + position: relative; +} + +.Form { + height: 100%; + overflow-y: scroll; +} + +.Form::-webkit-scrollbar { + display: none; +} + +.Label { + color: #555; + font-size: 1rem; + font-weight: 500; + margin-bottom: 0.5rem; +} + +.FormFields { + display: flex; + flex-direction: column; + row-gap: 1rem; +} + +.Buttons { + background-color: #fff; + padding: 1rem 0; + position: absolute; + bottom: 0; +} \ No newline at end of file diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx new file mode 100644 index 000000000..899e0d173 --- /dev/null +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -0,0 +1,132 @@ +import { Input } from 'components/UI/Form/Input/Input'; +import { AssistantOptions } from '../AssistantOptions/AssistantOptions'; +import { useEffect, useState } from 'react'; +import { Field, FormikProvider, useFormik } from 'formik'; +import { Autocomplete, TextField, Typography } from '@mui/material'; + +import styles from './CreateAssistant.module.css'; +import { Button } from 'components/UI/Form/Button/Button'; +import { useLazyQuery, useQuery } from '@apollo/client'; +import { GET_ASSISTANT, GET_MODELS } from 'graphql/queries/Assistant'; + +interface CreateAssistantProps { + currentId: string | number | null; +} + +export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { + const [name, setName] = useState(''); + const [model, setModel] = useState(); + const [prompt, setPrompt] = useState(''); + const [fileSearch, setFileSearch] = useState(true); + const [options, setOptions] = useState({ fileSearch: true, temperature: 1 }); + + let modelOptions = []; + + const { data: modelsList, loading: listLoading } = useQuery(GET_MODELS); + + const [getAssistant, { data, loading }] = useLazyQuery(GET_ASSISTANT); + + if (modelsList) { + modelOptions = modelsList?.listOpenaiModels; + } + + useEffect(() => { + if (currentId) { + getAssistant({ + variables: { assistantId: currentId }, + onCompleted: ({ assistant }) => { + setName(assistant?.assistant?.name); + setModel(assistant?.assistant?.model); + setPrompt(assistant?.assistant?.instructions); + }, + }); + } + }, [currentId]); + + const handleCreate = () => {}; + + const formFields = [ + { + component: Autocomplete, + name: 'model', + options: modelOptions, + optionLabel: 'name', + label: 'Model', + skipPayload: true, + helperText: 'Choose the best model for your needs', + multiple: false, + noOptionsText: loading ? 'Loading...' : 'No models found', + // renderInput: (params: any) => , + }, + { + component: Input, + name: 'name', + type: 'text', + label: 'Name', + helperText: 'Give a recognizable name for your assistant', + }, + { + component: Input, + name: 'prompt', + type: 'text', + label: 'Instructions', + rows: 3, + textArea: true, + helperText: 'Set the instructions according to your requirements.', + }, + { + component: AssistantOptions, + name: 'options', + fileSearch, + options, + }, + ]; + + const states = { + name, + model, + prompt, + fileSearch, + options, + }; + + console.log(states); + + const FormSchema = {}; + + const formik = useFormik({ + initialValues: states, + validationSchema: FormSchema, + enableReinitialize: true, + onSubmit: (values, { setErrors }) => {}, + }); + + return ( + +
+
+
+ {formFields.map((field: any) => ( +
+ + {field.label} + + + +
+ ))} +
+
+ + + +
+
+
+
+ ); +}; diff --git a/src/containers/Assistants/ListItems/List.module.css b/src/containers/Assistants/ListItems/List.module.css new file mode 100644 index 000000000..f43da4813 --- /dev/null +++ b/src/containers/Assistants/ListItems/List.module.css @@ -0,0 +1,93 @@ +.Container { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; +} + +.ListContainer { + position: relative; + margin-top: 2rem; + height: 100%; + display: flex; + flex-direction: column; + row-gap: 0.5rem; + overflow-y: scroll; +} + +.Item { + background-color: #fff; + padding: 0.8rem 1rem; + border-radius: 8px; + display: flex; + width: 100%; + cursor: pointer; +} + +.Item:hover { + background-color: #ebf8ee; +} + +.Itemm { + display: flex; + flex-direction: column; + width: 100%; +} + +.SelectedItem { + background-color: #ebf8ee; +} + +.Header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: flex-end; +} + +.Title { + font-size: 16px; + font-weight: 600; +} + +.Date { + font-size: 10px; + color: #0000007a; +} + +.Id { + font-size: 12px; + color: #555; + display: flex; + align-items: center; + column-gap: 1rem; +} + +.Id button { + padding: 0; +} + +.Id svg { + height: 1rem !important; +} + +.NoItems { + color: #555; + font-size: 1rem; + width: 100%; + text-align: center; +} + +.LoadMore { + width: fit-content; + background-color: #073f24; + padding: 4px 8px; + margin: 0 auto; + margin-top: 1rem; + border-radius: 12px 12px 0 0; + color: #edf6f2; + cursor: pointer; + font-weight: 500; + font-size: 0.875rem; + z-index: 101; +} \ No newline at end of file diff --git a/src/containers/Assistants/ListItems/List.tsx b/src/containers/Assistants/ListItems/List.tsx new file mode 100644 index 000000000..c1a0ea844 --- /dev/null +++ b/src/containers/Assistants/ListItems/List.tsx @@ -0,0 +1,139 @@ +import { DocumentNode, useQuery } from '@apollo/client'; +import { IconButton } from '@mui/material'; +import dayjs from 'dayjs'; +import { useEffect, useState } from 'react'; + +import CopyIcon from 'assets/images/icons/Settings/Copy.svg?react'; +import { DEFAULT_ENTITY_LIMIT, DEFAULT_MESSAGE_LOADMORE_LIMIT } from 'common/constants'; +import { copyToClipboard } from 'common/utils'; +import SearchBar from 'components/UI/SearchBar/SearchBar'; + +import styles from './List.module.css'; + +interface ListProps { + icon?: any; + getItemsQuery: DocumentNode; + listItemName: string; + currentId?: any; + refreshList?: boolean; + setCurrentId: any; +} + +export const List = ({ + icon, + getItemsQuery, + listItemName, + refreshList, + setCurrentId, + currentId, +}: ListProps) => { + const [searchTerm, setSearchTerm] = useState(''); + const [showLoadMore, setLoadMore] = useState(false); + + const { data, refetch, fetchMore } = useQuery(getItemsQuery, { + variables: { + filter: { + name: searchTerm, + }, + opts: { + limit: DEFAULT_ENTITY_LIMIT, + order: 'DESC', + }, + }, + onCompleted: (data) => { + // setCurrentId(data[listItemName][0]?.id); + if (data[listItemName].length > DEFAULT_ENTITY_LIMIT - 1) { + setLoadMore(true); + } + }, + }); + + const loadMoreItems = () => { + const variables = { + filter: { + name: searchTerm, + }, + opts: { + limit: DEFAULT_MESSAGE_LOADMORE_LIMIT, + offset: data[listItemName].length, + order: 'DESC', + }, + }; + + fetchMore({ + variables, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + if (fetchMoreResult[listItemName].length === 0) setLoadMore(false); + + const updatedItems = [...prev[listItemName], ...fetchMoreResult[listItemName]]; + + return { + ...prev, + [listItemName]: updatedItems, + }; + }, + }); + }; + + useEffect(() => { + refetch(); + }, [refreshList]); + + return ( +
+
+ { + setSearchTerm(e.target.value); + }} + onReset={() => setSearchTerm('')} + searchMode + iconFront + /> +
+
+ {data && + data[listItemName] && + (data[listItemName].length === 0 ? ( +
No {listItemName} found!
+ ) : ( + data[listItemName].map((item: any) => ( +
setCurrentId(item.id)} + data-testid="listItem" + > + {icon &&
{icon}
} +
+
+ {item.name} + + {dayjs(item.insertedAt).format('DD/MM/YY, HH:MM')} + +
+ + copyToClipboard(item.itemId)} + edge="end" + > + + + {item.itemId} + +
+
+ )) + ))} + {showLoadMore ? ( + + Load More + + ) : null} +
+
+ ); +}; diff --git a/src/graphql/mutations/Assistant.ts b/src/graphql/mutations/Assistant.ts new file mode 100644 index 000000000..8571fa311 --- /dev/null +++ b/src/graphql/mutations/Assistant.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +export const CREATE_ASSISTANT = gql` + mutation CreateAssistant($input: AssistantInput) { + createAssistant(input: $input) { + assistant { + id + name + } + } + } +`; diff --git a/src/graphql/queries/Assistant.ts b/src/graphql/queries/Assistant.ts new file mode 100644 index 000000000..e41cf23fe --- /dev/null +++ b/src/graphql/queries/Assistant.ts @@ -0,0 +1,35 @@ +import { gql } from '@apollo/client'; + +export const GET_ASSISTANTS = gql` + query Assistants($filter: AssistantFilter, $opts: Opts) { + assistants(filter: $filter, opts: $opts) { + itemId: assistantId + id + insertedAt + name + } + } +`; + +export const GET_ASSISTANT = gql` + query Assistant($assistantId: ID!) { + assistant(id: $assistantId) { + assistant { + assistantId + id + insertedAt + instructions + model + name + temperature + updatedAt + } + } + } +`; + +export const GET_MODELS = gql` + query RootQueryType { + listOpenaiModels + } +`; diff --git a/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx b/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx index cb51d5b93..c750a2e16 100644 --- a/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx +++ b/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx @@ -73,6 +73,7 @@ const InteractiveMessage = lazy(() => import('containers/InteractiveMessage/Inte const RoleList = lazy(() => import('containers/Role/RoleList/RoleList')); const Role = lazy(() => import('containers/Role/Role')); const KnowledgeBase = lazy(() => import('containers/KnowledgeBase/KnowledgeBase')); +const Assistant = lazy(() => import('containers/Assistants/Assistants')); const routeStaff = ( @@ -156,6 +157,8 @@ const routeAdmin = ( } /> + } /> + } /> ); From 5c6fe0b4b5b3af330ee7066608fe64210deb48c7 Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Wed, 16 Oct 2024 08:36:16 +0530 Subject: [PATCH 02/18] connected assistant apis --- src/assets/images/database.svg | 9 + .../UI/Form/AutoComplete/AutoComplete.tsx | 3 - .../AssistantOptions.module.css | 125 ++++++++- .../AssistantOptions/AssistantOptions.tsx | 239 +++++++++++++++--- .../CreateAssistant/CreateAssistant.tsx | 121 ++++++--- src/containers/Assistants/ListItems/List.tsx | 2 +- src/graphql/mutations/Assistant.ts | 48 ++++ src/graphql/queries/Assistant.ts | 18 ++ 8 files changed, 486 insertions(+), 79 deletions(-) create mode 100644 src/assets/images/database.svg diff --git a/src/assets/images/database.svg b/src/assets/images/database.svg new file mode 100644 index 000000000..0a865d599 --- /dev/null +++ b/src/assets/images/database.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/UI/Form/AutoComplete/AutoComplete.tsx b/src/components/UI/Form/AutoComplete/AutoComplete.tsx index 32ba5f7cb..04b0a33e1 100644 --- a/src/components/UI/Form/AutoComplete/AutoComplete.tsx +++ b/src/components/UI/Form/AutoComplete/AutoComplete.tsx @@ -91,7 +91,6 @@ export const AutoComplete = ({ const [inputValue, setInputValue] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [open, setOpen] = useState(false); - console.log(field); let classStyles = { ...classes, inputRoot: styles.DefaultInputRoot }; @@ -115,8 +114,6 @@ export const AutoComplete = ({ return []; } - console.log(field.value); - return field.value; })(); diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css b/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css index 87aab5571..c13f015b3 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.module.css @@ -2,7 +2,6 @@ margin: 1rem 0; display: flex; flex-direction: column; - row-gap: 1rem; } .Label { @@ -14,10 +13,16 @@ column-gap: 0.5rem; } +.Files { + padding-bottom: 1rem; + border-bottom: 1px solid #e5e2e2; +} + .Temperature { display: flex; column-gap: 1rem; align-items: center; + padding: 1rem 0; } .Slider { @@ -33,8 +38,126 @@ border: 0.5px solid #a4a4a4; border-radius: 4px; width: 15%; + text-align: center; } .SliderDisplay:focus { outline: none; +} + +.FilesHeader { + display: flex; + justify-content: space-between; +} + +.FilesHeader button { + border-radius: 10px; + font-size: 1rem; + padding: 2px 8px; + display: flex; + align-items: center; + column-gap: 0.5rem; +} + +.LabelContainer { + display: flex; + align-items: center; + column-gap: 0.5rem; +} + +.DialogContent { + text-align: center; + max-height: 40vh; + display: flex; + flex-direction: column; +} + +.DialogContent span { + font-size: 0.8rem; + line-height: 0; + color: #555; + margin: 1rem 0; +} + +.DialogContent span a { + color: #119656; + font-weight: 600; +} + +.UploadContainer { + padding: 1rem 0; + display: flex; + align-items: flex-start; + justify-content: center; + border: 1px solid #119656; + border-style: dashed; + width: 100%; +} + +.UploadContainer input { + display: none; +} + +.HelperText { + margin: 0px; + color: #93a29b; + line-height: 1.5; + font-size: 12px; + margin-top: 7px; + padding: 0 8px; +} + +.FileList { + display: flex; + flex-direction: column; + row-gap: 0.5rem; + margin: 1rem 0; + height: 100%; + overflow-y: scroll; +} + +.File { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.8rem; + background-color: #ebf8ee; + padding: 0.5rem 1rem; + padding-right: 0; +} + +.File div { + display: flex; + align-items: center; + column-gap: 0.8rem; +} + +.EmptyText { + font-size: 0.8rem; + color: #2f2f2f; + width: 100%; + text-align: center; +} + +.VectorStore { + display: flex; + width: 100%; + padding: 1rem 2rem; + background-color: #ebf8ee; + font-size: 0.8rem; + padding: 0.5rem 1rem; + margin: 1rem 0; + justify-content: space-between; +} + +.VectorContent { + display: flex; + align-items: center; + column-gap: 1rem; +} + +.VectorStore p { + font-weight: 600; + margin: 0; + font-size: 1rem; } \ No newline at end of file diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 99c3a4678..1a0620f67 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -1,12 +1,33 @@ -import { FormControlLabel, Slider, Switch, Typography } from '@mui/material'; +import { + Button, + CircularProgress, + FormControlLabel, + IconButton, + Slider, + Switch, + Typography, +} from '@mui/material'; import { useState } from 'react'; import styles from './AssistantOptions.module.css'; import HelpIcon from 'components/UI/HelpIcon/HelpIcon'; - +import AddIcon from 'assets/images/AddGreenIcon.svg?react'; +import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; +import DatabaseIcon from 'assets/images/database.svg?react'; +import { DialogBox } from 'components/UI/DialogBox/DialogBox'; +import DeleteIcon from 'assets/images/icons/Delete/Red.svg?react'; +import { useMutation, useQuery } from '@apollo/client'; +import { + ADD_FILES_TO_FILE_SEARCH, + REMOVE_FILES_FROM_ASSISTANT, + UPLOAD_FILE_TO_OPENAI, +} from 'graphql/mutations/Assistant'; +import { GET_ASSISTANT_FILES } from 'graphql/queries/Assistant'; +import { setNotification } from 'common/notification'; interface AssistantOptionsProps { - form: any; - field: any; + options: any; fileSearch: boolean; + currentId: any; + setOptions: any; } const temperatureInfo = @@ -15,41 +36,185 @@ const temperatureInfo = const fileSearchInfo = 'File search enables the assistant with knowledge from files that you or your users upload. Once a file is uploaded, the assistant automatically decides when to retrieve content based on user requests.'; -export const AssistantOptions = ({ form, field, fileSearch }: AssistantOptionsProps) => { - const [checked, setChecked] = useState(false); +export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOptionsProps) => { + const [showUploadDialog, setShowUploadDialog] = useState(false); + const [files, setFiles] = useState([]); + + const [uploadFile, { loading: uploadingFile }] = useMutation(UPLOAD_FILE_TO_OPENAI); + const [addFilesToFileSearch, { loading: addingFiles }] = useMutation(ADD_FILES_TO_FILE_SEARCH); + const [removeFile] = useMutation(REMOVE_FILES_FROM_ASSISTANT); + + const { refetch, data } = useQuery(GET_ASSISTANT_FILES, { + variables: { assistantId: currentId }, + onCompleted: ({ assistant }) => { + const attachedFiles = assistant.assistant.vectorStore.files; + setFiles([...files, ...attachedFiles.map((item: any) => ({ ...item, attached: true }))]); + }, + }); + + const handleFileChange = (event: any) => { + if (event.target.files.length === 0) return; + + uploadFile({ + variables: { + media: event.target.files[0], + }, + onCompleted: ({ uploadFilesearchFile }) => { + uploadFilesearchFile = { + fileId: uploadFilesearchFile?.fileId, + filename: uploadFilesearchFile?.filename, + }; + setFiles([...files, uploadFilesearchFile]); + }, + }); + }; + + const handleRemoveFile = (file: any) => { + if (file.attached) { + removeFile({ + variables: { + fileId: file.fileId, + removeAssistantFileId: currentId, + }, + onCompleted: () => { + refetch(); + }, + }); + } + setFiles(files.filter((fileItem) => fileItem.fileId !== file.fileId)); + }; + + const handleFileUpload = () => { + const filesToUpload = files.filter((item) => !item.attached); + if (filesToUpload.length > 0) { + addFilesToFileSearch({ + variables: { + addAssistantFilesId: currentId, + mediaInfo: files.filter((item) => !item.attached), + }, + onCompleted: (data) => { + setNotification('Files added to assistant!', 'success'); + setShowUploadDialog(false); + }, + }); + } else { + setShowUploadDialog(false); + } + }; + + let dialog; + if (showUploadDialog) { + dialog = ( + setShowUploadDialog(false)} + buttonOk="Add" + fullWidth + handleOk={handleFileUpload} + disableOk={addingFiles} + buttonOkLoading={addingFiles} + > +
+ + {files.length > 0 && ( +
+ {files.map((file, index) => ( +
+ {file.filename} + handleRemoveFile(file)}> + + +
+ ))} +
+ )} + + Information in the attached files will be available to this assistant.{' '} + Learn More + +
+
+ ); + } return (
-
+
Tools - { - form.setFieldValue('options', { - ...field.value, - fileSearch: event.target.checked, - }); - }} - /> - } - label={ - <> - File Search{' '} - + { + setOptions({ + ...options, + fileSearch: event.target.checked, + }); }} /> - - } - /> + } + label={ +
+ File Search + +
+ } + /> - + +
+ {data?.assistant.assistant.vectorStore && ( +
+
+ +
+

{data?.assistant.assistant.vectorStore?.name}

+ {data?.assistant.assistant.vectorStore?.vectorStoreId} +
+
+ {}}> + + +
+ )}
@@ -65,20 +230,20 @@ export const AssistantOptions = ({ form, field, fileSearch }: AssistantOptionsPr
{ - form.setFieldValue('options', { - ...field.value, + setOptions({ + ...options, temperature: value, }); }} - value={field.value.temperature} + value={options.temperature} step={0.01} max={2} /> { - form.setFieldValue('options', { - ...field.value, + setOptions({ + ...options, temperature: event.target.value, }); }} @@ -86,6 +251,8 @@ export const AssistantOptions = ({ form, field, fileSearch }: AssistantOptionsPr />
+ + {dialog}
); }; diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index 899e0d173..35b99ad5c 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -6,8 +6,13 @@ import { Autocomplete, TextField, Typography } from '@mui/material'; import styles from './CreateAssistant.module.css'; import { Button } from 'components/UI/Form/Button/Button'; -import { useLazyQuery, useQuery } from '@apollo/client'; +import { useLazyQuery, useMutation, useQuery } from '@apollo/client'; import { GET_ASSISTANT, GET_MODELS } from 'graphql/queries/Assistant'; +import { AutoComplete } from 'components/UI/Form/AutoComplete/AutoComplete'; +import * as Yup from 'yup'; +import { Loading } from 'components/UI/Layout/Loading/Loading'; +import { CREATE_ASSISTANT, UPDATE_ASSISTANT } from 'graphql/mutations/Assistant'; +import { setNotification } from 'common/notification'; interface CreateAssistantProps { currentId: string | number | null; @@ -15,48 +20,89 @@ interface CreateAssistantProps { export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { const [name, setName] = useState(''); - const [model, setModel] = useState(); - const [prompt, setPrompt] = useState(''); - const [fileSearch, setFileSearch] = useState(true); + const [model, setModel] = useState(null); + const [instructions, setPrompt] = useState(''); const [options, setOptions] = useState({ fileSearch: true, temperature: 1 }); + const states = { + name, + model, + instructions, + options, + }; + let modelOptions = []; const { data: modelsList, loading: listLoading } = useQuery(GET_MODELS); + const [getAssistant, { loading }] = useLazyQuery(GET_ASSISTANT); - const [getAssistant, { data, loading }] = useLazyQuery(GET_ASSISTANT); + const [updateAssistant, { loading: savingChanges }] = useMutation(UPDATE_ASSISTANT, { + onCompleted: () => { + setNotification('Changes saved successfully', 'success'); + }, + }); if (modelsList) { - modelOptions = modelsList?.listOpenaiModels; + modelOptions = modelsList?.listOpenaiModels.map((item: string, index: number) => ({ + id: index.toString(), + label: item, + })); } useEffect(() => { - if (currentId) { + if (currentId && modelsList) { getAssistant({ variables: { assistantId: currentId }, onCompleted: ({ assistant }) => { - setName(assistant?.assistant?.name); - setModel(assistant?.assistant?.model); - setPrompt(assistant?.assistant?.instructions); + setName(assistant?.assistant?.name || ''); + let modelValue = modelOptions?.find( + (item: any) => item.label === assistant?.assistant?.model + ); + setModel(modelValue); + setPrompt(assistant?.assistant?.instructions || ''); + setOptions({ + ...options, + temperature: assistant?.assistant?.temperature, + }); }, }); } - }, [currentId]); - - const handleCreate = () => {}; + }, [currentId, modelsList]); + + const handleCreate = () => { + console.log(states); + const { + instructions: instructionsValue, + model: modelValue, + name: nameValue, + options: optionsValue, + } = states; + + const payload = { + instructions: instructionsValue, + model: modelValue.label, + name: nameValue, + temperature: optionsValue?.temperature, + }; + + updateAssistant({ + variables: { + updateAssistantId: currentId, + input: payload, + }, + }); + }; - const formFields = [ + const formFields: any = [ { - component: Autocomplete, + component: AutoComplete, name: 'model', - options: modelOptions, - optionLabel: 'name', - label: 'Model', - skipPayload: true, - helperText: 'Choose the best model for your needs', + options: modelOptions || [], + optionLabel: 'label', multiple: false, - noOptionsText: loading ? 'Loading...' : 'No models found', - // renderInput: (params: any) => , + label: 'Model', + helperText: 'Use this to categorize your flows.', + onChange: (value: any) => setModel(value), }, { component: Input, @@ -64,35 +110,32 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { type: 'text', label: 'Name', helperText: 'Give a recognizable name for your assistant', + onChange: (value: any) => setName(value), }, { component: Input, - name: 'prompt', + name: 'instructions', type: 'text', label: 'Instructions', rows: 3, textArea: true, helperText: 'Set the instructions according to your requirements.', + onChange: (value: any) => setPrompt(value), }, { component: AssistantOptions, name: 'options', - fileSearch, options, + currentId, + setOptions, }, ]; - const states = { - name, - model, - prompt, - fileSearch, - options, - }; - - console.log(states); - - const FormSchema = {}; + const FormSchema = Yup.object().shape({ + name: Yup.string(), + model: Yup.object().required('Model is required'), + instructions: Yup.string(), + }); const formik = useFormik({ initialValues: states, @@ -101,6 +144,9 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { onSubmit: (values, { setErrors }) => {}, }); + if (loading || listLoading) { + return ; + } return (
@@ -117,10 +163,9 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { ))}
- - diff --git a/src/containers/Assistants/ListItems/List.tsx b/src/containers/Assistants/ListItems/List.tsx index c1a0ea844..a43fd7399 100644 --- a/src/containers/Assistants/ListItems/List.tsx +++ b/src/containers/Assistants/ListItems/List.tsx @@ -41,7 +41,7 @@ export const List = ({ }, }, onCompleted: (data) => { - // setCurrentId(data[listItemName][0]?.id); + setCurrentId(data[listItemName][0]?.id); if (data[listItemName].length > DEFAULT_ENTITY_LIMIT - 1) { setLoadMore(true); } diff --git a/src/graphql/mutations/Assistant.ts b/src/graphql/mutations/Assistant.ts index 8571fa311..1997ce9ac 100644 --- a/src/graphql/mutations/Assistant.ts +++ b/src/graphql/mutations/Assistant.ts @@ -10,3 +10,51 @@ export const CREATE_ASSISTANT = gql` } } `; + +export const UPDATE_ASSISTANT = gql` + mutation UpdateAssistant($updateAssistantId: ID!, $input: AssistantInput) { + updateAssistant(id: $updateAssistantId, input: $input) { + errors { + message + key + } + } + } +`; + +export const UPLOAD_FILE_TO_OPENAI = gql` + mutation UploadFilesearchFile($media: Upload!) { + uploadFilesearchFile(media: $media) { + fileId + filename + } + } +`; + +export const ADD_FILES_TO_FILE_SEARCH = gql` + mutation AddAssistantFiles($addAssistantFilesId: ID!, $mediaInfo: [FileInfoInput!]!) { + addAssistantFiles(id: $addAssistantFilesId, mediaInfo: $mediaInfo) { + assistant { + id + } + errors { + key + message + } + } + } +`; + +export const REMOVE_FILES_FROM_ASSISTANT = gql` + mutation RemoveAssistantFile($fileId: String!, $removeAssistantFileId: ID!) { + removeAssistantFile(fileId: $fileId, id: $removeAssistantFileId) { + assistant { + id + } + errors { + message + key + } + } + } +`; diff --git a/src/graphql/queries/Assistant.ts b/src/graphql/queries/Assistant.ts index e41cf23fe..96dbda8c4 100644 --- a/src/graphql/queries/Assistant.ts +++ b/src/graphql/queries/Assistant.ts @@ -28,6 +28,24 @@ export const GET_ASSISTANT = gql` } `; +export const GET_ASSISTANT_FILES = gql` + query Assistant($assistantId: ID!) { + assistant(id: $assistantId) { + assistant { + vectorStore { + files { + fileId: id + filename: name + } + id + name + vectorStoreId + } + } + } + } +`; + export const GET_MODELS = gql` query RootQueryType { listOpenaiModels From 995f37e3f8447eed685be9a2d7591a797db4b59d Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Wed, 16 Oct 2024 11:04:22 +0530 Subject: [PATCH 03/18] minor refactoring --- src/assets/images/CopyGreen.svg | 4 + .../AssistantOptions/AssistantOptions.tsx | 3 +- src/containers/Assistants/Assistants.tsx | 12 ++- .../CreateAssistant.module.css | 30 +++++++ .../CreateAssistant/CreateAssistant.tsx | 79 +++++++++++++++++-- src/containers/Assistants/ListItems/List.tsx | 2 +- src/graphql/mutations/Assistant.ts | 14 ++++ 7 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 src/assets/images/CopyGreen.svg diff --git a/src/assets/images/CopyGreen.svg b/src/assets/images/CopyGreen.svg new file mode 100644 index 000000000..ccc482ec5 --- /dev/null +++ b/src/assets/images/CopyGreen.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 1a0620f67..23afb04bd 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -95,6 +95,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp onCompleted: (data) => { setNotification('Files added to assistant!', 'success'); setShowUploadDialog(false); + refetch(); }, }); } else { @@ -152,7 +153,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp
)} - Information in the attached files will be available to this assistant.{' '} + Information in the attached files will be available to this assistant. Learn More diff --git a/src/containers/Assistants/Assistants.tsx b/src/containers/Assistants/Assistants.tsx index ce8f1f098..345186437 100644 --- a/src/containers/Assistants/Assistants.tsx +++ b/src/containers/Assistants/Assistants.tsx @@ -11,7 +11,6 @@ import { CREATE_ASSISTANT } from 'graphql/mutations/Assistant'; export const Assistants = () => { const [updateList, setUpdateList] = useState(false); const [assistantId, setAssistantId] = useState(null); - const [showCreateAssistant, setShowCreateAssistant] = useState(false); const [createAssistant] = useMutation(CREATE_ASSISTANT); @@ -22,7 +21,8 @@ export const Assistants = () => { name: null, }, }, - onCompleted: () => { + onCompleted: ({ createAssistant }) => { + setAssistantId(createAssistant.assistant.id); setUpdateList(!updateList); }, }); @@ -43,11 +43,17 @@ export const Assistants = () => { listItemName="assistants" refreshList={updateList} setCurrentId={setAssistantId} + currentId={assistantId} />
{assistantId ? ( - + ) : (

Select/Create an assistant

)} diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css b/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css index ce22aead3..fc28d7dd8 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css @@ -31,4 +31,34 @@ padding: 1rem 0; position: absolute; bottom: 0; +} + +.DialogContent { + max-width: auto; + margin-top: 0px; + text-align: center; + color: #073f24; + font-weight: 400; + display: flex; + justify-content: center; +} + +.AssistantId { + color: #119656; + font-weight: 600; + display: flex; + justify-content: space-between; + width: 100%; +} + +.AssistantId div { + display: flex; + column-gap: 0.5rem; + align-items: center; + cursor: pointer; +} + +.HelperText { + color: #999999; + font-weight: normal; } \ No newline at end of file diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index 35b99ad5c..fd6ba6374 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -11,18 +11,32 @@ import { GET_ASSISTANT, GET_MODELS } from 'graphql/queries/Assistant'; import { AutoComplete } from 'components/UI/Form/AutoComplete/AutoComplete'; import * as Yup from 'yup'; import { Loading } from 'components/UI/Layout/Loading/Loading'; -import { CREATE_ASSISTANT, UPDATE_ASSISTANT } from 'graphql/mutations/Assistant'; +import { CREATE_ASSISTANT, DELETE_ASSISTANT, UPDATE_ASSISTANT } from 'graphql/mutations/Assistant'; import { setNotification } from 'common/notification'; +import { DialogBox } from 'components/UI/DialogBox/DialogBox'; +import { copyToClipboard } from 'common/utils'; +import { IconButton } from '@mui/material'; +import CopyIcon from 'assets/images/CopyGreen.svg?react'; interface CreateAssistantProps { currentId: string | number | null; + updateList: boolean; + setCurrentId: any; + setUpdateList: any; } -export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { +export const CreateAssistant = ({ + currentId, + setUpdateList, + setCurrentId, + updateList, +}: CreateAssistantProps) => { + const [assistantId, setAssistantId] = useState(''); const [name, setName] = useState(''); const [model, setModel] = useState(null); - const [instructions, setPrompt] = useState(''); + const [instructions, setInstructions] = useState(''); const [options, setOptions] = useState({ fileSearch: true, temperature: 1 }); + const [showConfirmation, setShowConfirmation] = useState(false); const states = { name, @@ -42,6 +56,8 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { }, }); + const [deleteAssistant, { loading: deletingAssistant }] = useMutation(DELETE_ASSISTANT); + if (modelsList) { modelOptions = modelsList?.listOpenaiModels.map((item: string, index: number) => ({ id: index.toString(), @@ -54,12 +70,13 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { getAssistant({ variables: { assistantId: currentId }, onCompleted: ({ assistant }) => { + setAssistantId(assistant?.assistant?.assistantId); setName(assistant?.assistant?.name || ''); let modelValue = modelOptions?.find( (item: any) => item.label === assistant?.assistant?.model ); setModel(modelValue); - setPrompt(assistant?.assistant?.instructions || ''); + setInstructions(assistant?.assistant?.instructions || ''); setOptions({ ...options, temperature: assistant?.assistant?.temperature, @@ -70,7 +87,6 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { }, [currentId, modelsList]); const handleCreate = () => { - console.log(states); const { instructions: instructionsValue, model: modelValue, @@ -109,8 +125,16 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { name: 'name', type: 'text', label: 'Name', - helperText: 'Give a recognizable name for your assistant', onChange: (value: any) => setName(value), + helperText: ( +
+ Give a recognizable name for your assistant +
copyToClipboard(assistantId)}> + + {assistantId} +
+
+ ), }, { component: Input, @@ -120,7 +144,7 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { rows: 3, textArea: true, helperText: 'Set the instructions according to your requirements.', - onChange: (value: any) => setPrompt(value), + onChange: (value: any) => setInstructions(value), }, { component: AssistantOptions, @@ -144,6 +168,44 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { onSubmit: (values, { setErrors }) => {}, }); + const handleClose = () => { + setShowConfirmation(false); + }; + + const handleDelete = () => { + deleteAssistant({ + variables: { + deleteAssistantId: currentId, + }, + onCompleted: ({ deleteAssistant }) => { + setShowConfirmation(false); + setNotification( + `Assistant ${deleteAssistant.assistant.name} deleted successfully`, + 'success' + ); + setCurrentId(null); + setUpdateList(!updateList); + }, + }); + }; + + let dialog; + if (showConfirmation) { + dialog = ( + +
You won't be able to use this assistant.
+
+ ); + } + if (loading || listLoading) { return ; } @@ -166,11 +228,12 @@ export const CreateAssistant = ({ currentId }: CreateAssistantProps) => { -
+ {dialog}
); diff --git a/src/containers/Assistants/ListItems/List.tsx b/src/containers/Assistants/ListItems/List.tsx index a43fd7399..bdceda26f 100644 --- a/src/containers/Assistants/ListItems/List.tsx +++ b/src/containers/Assistants/ListItems/List.tsx @@ -41,7 +41,7 @@ export const List = ({ }, }, onCompleted: (data) => { - setCurrentId(data[listItemName][0]?.id); + if (!currentId) setCurrentId(data[listItemName][0]?.id); if (data[listItemName].length > DEFAULT_ENTITY_LIMIT - 1) { setLoadMore(true); } diff --git a/src/graphql/mutations/Assistant.ts b/src/graphql/mutations/Assistant.ts index 1997ce9ac..456535c47 100644 --- a/src/graphql/mutations/Assistant.ts +++ b/src/graphql/mutations/Assistant.ts @@ -58,3 +58,17 @@ export const REMOVE_FILES_FROM_ASSISTANT = gql` } } `; + +export const DELETE_ASSISTANT = gql` + mutation DeleteAssistant($deleteAssistantId: ID!) { + deleteAssistant(id: $deleteAssistantId) { + assistant { + name + assistantId + } + errors { + message + } + } + } +`; From 49ce86150725328589bbcd1ad79ab305e061b69e Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Wed, 16 Oct 2024 11:12:06 +0530 Subject: [PATCH 04/18] deepscan fixes --- .../AssistantOptions/AssistantOptions.tsx | 23 +++++++++---- src/containers/Assistants/Assistants.tsx | 16 ++++++---- .../CreateAssistant/CreateAssistant.tsx | 32 +++++++++++-------- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 23afb04bd..404e74e84 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -1,3 +1,4 @@ +import { useMutation, useQuery } from '@apollo/client'; import { Button, CircularProgress, @@ -8,21 +9,26 @@ import { Typography, } from '@mui/material'; import { useState } from 'react'; -import styles from './AssistantOptions.module.css'; -import HelpIcon from 'components/UI/HelpIcon/HelpIcon'; + import AddIcon from 'assets/images/AddGreenIcon.svg?react'; -import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; import DatabaseIcon from 'assets/images/database.svg?react'; -import { DialogBox } from 'components/UI/DialogBox/DialogBox'; import DeleteIcon from 'assets/images/icons/Delete/Red.svg?react'; -import { useMutation, useQuery } from '@apollo/client'; +import FileIcon from 'assets/images/FileGreen.svg?react'; +import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; + +import { DialogBox } from 'components/UI/DialogBox/DialogBox'; +import HelpIcon from 'components/UI/HelpIcon/HelpIcon'; +import { setNotification } from 'common/notification'; + import { ADD_FILES_TO_FILE_SEARCH, REMOVE_FILES_FROM_ASSISTANT, UPLOAD_FILE_TO_OPENAI, } from 'graphql/mutations/Assistant'; + import { GET_ASSISTANT_FILES } from 'graphql/queries/Assistant'; -import { setNotification } from 'common/notification'; + +import styles from './AssistantOptions.module.css'; interface AssistantOptionsProps { options: any; fileSearch: boolean; @@ -144,7 +150,10 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp
{files.map((file, index) => (
- {file.filename} +
+ + {file.filename} +
handleRemoveFile(file)}> diff --git a/src/containers/Assistants/Assistants.tsx b/src/containers/Assistants/Assistants.tsx index 345186437..e58728988 100644 --- a/src/containers/Assistants/Assistants.tsx +++ b/src/containers/Assistants/Assistants.tsx @@ -1,12 +1,16 @@ +import { useMutation } from '@apollo/client'; +import { useState } from 'react'; + import { assistantsInfo } from 'common/HelpData'; + import { Heading } from 'components/UI/Heading/Heading'; -import styles from './Assistants.module.css'; -import { useState } from 'react'; -import { List } from './ListItems/List'; -import { CreateAssistant } from './CreateAssistant/CreateAssistant'; -import { GET_ASSISTANTS } from 'graphql/queries/Assistant'; -import { useMutation } from '@apollo/client'; + import { CREATE_ASSISTANT } from 'graphql/mutations/Assistant'; +import { GET_ASSISTANTS } from 'graphql/queries/Assistant'; + +import { CreateAssistant } from './CreateAssistant/CreateAssistant'; +import { List } from './ListItems/List'; +import styles from './Assistants.module.css'; export const Assistants = () => { const [updateList, setUpdateList] = useState(false); diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index fd6ba6374..7fd70a91e 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -1,23 +1,27 @@ -import { Input } from 'components/UI/Form/Input/Input'; -import { AssistantOptions } from '../AssistantOptions/AssistantOptions'; -import { useEffect, useState } from 'react'; -import { Field, FormikProvider, useFormik } from 'formik'; -import { Autocomplete, TextField, Typography } from '@mui/material'; - -import styles from './CreateAssistant.module.css'; -import { Button } from 'components/UI/Form/Button/Button'; import { useLazyQuery, useMutation, useQuery } from '@apollo/client'; -import { GET_ASSISTANT, GET_MODELS } from 'graphql/queries/Assistant'; -import { AutoComplete } from 'components/UI/Form/AutoComplete/AutoComplete'; +import { Typography } from '@mui/material'; +import { Field, FormikProvider, useFormik } from 'formik'; +import { useEffect, useState } from 'react'; import * as Yup from 'yup'; -import { Loading } from 'components/UI/Layout/Loading/Loading'; -import { CREATE_ASSISTANT, DELETE_ASSISTANT, UPDATE_ASSISTANT } from 'graphql/mutations/Assistant'; + +import { copyToClipboard } from 'common/utils'; import { setNotification } from 'common/notification'; + +import { AutoComplete } from 'components/UI/Form/AutoComplete/AutoComplete'; +import { Button } from 'components/UI/Form/Button/Button'; import { DialogBox } from 'components/UI/DialogBox/DialogBox'; -import { copyToClipboard } from 'common/utils'; -import { IconButton } from '@mui/material'; +import { Input } from 'components/UI/Form/Input/Input'; +import { Loading } from 'components/UI/Layout/Loading/Loading'; + +import { GET_ASSISTANT, GET_MODELS } from 'graphql/queries/Assistant'; +import { DELETE_ASSISTANT, UPDATE_ASSISTANT } from 'graphql/mutations/Assistant'; + import CopyIcon from 'assets/images/CopyGreen.svg?react'; +import { AssistantOptions } from '../AssistantOptions/AssistantOptions'; + +import styles from './CreateAssistant.module.css'; + interface CreateAssistantProps { currentId: string | number | null; updateList: boolean; From 652006d9bd0515c4f320ec403debff2e41f293d4 Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Wed, 16 Oct 2024 21:10:14 +0530 Subject: [PATCH 05/18] fixed create page --- src/containers/Assistants/CreateAssistant/CreateAssistant.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index 7fd70a91e..1060aae37 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -110,6 +110,9 @@ export const CreateAssistant = ({ updateAssistantId: currentId, input: payload, }, + onCompleted: (data) => { + setUpdateList(!updateList); + }, }); }; From 21a0c56c15f5e9d9e48c6ad909fd03531ce823cf Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Thu, 17 Oct 2024 09:13:30 +0530 Subject: [PATCH 06/18] added test cases for assistants --- .../AssistantOptions/AssistantOptions.tsx | 6 +- src/containers/Assistants/Assistants.test.tsx | 58 +++++++ src/containers/Assistants/Assistants.tsx | 12 +- .../CreateAssistant/CreateAssistant.tsx | 5 +- src/containers/Assistants/ListItems/List.tsx | 4 +- src/mocks/Assistants.ts | 151 ++++++++++++++++++ .../AuthenticatedRoute/AuthenticatedRoute.tsx | 1 + 7 files changed, 230 insertions(+), 7 deletions(-) create mode 100644 src/containers/Assistants/Assistants.test.tsx create mode 100644 src/mocks/Assistants.ts diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 404e74e84..e59809290 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -220,9 +220,9 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp {data?.assistant.assistant.vectorStore?.vectorStoreId}
- {}}> - - + {data?.assistant.assistant.vectorStore.files.length > 0 && ( + {data?.assistant.assistant.vectorStore.files.length} files + )} )} diff --git a/src/containers/Assistants/Assistants.test.tsx b/src/containers/Assistants/Assistants.test.tsx new file mode 100644 index 000000000..3b6ff1add --- /dev/null +++ b/src/containers/Assistants/Assistants.test.tsx @@ -0,0 +1,58 @@ +import { MockedProvider } from '@apollo/client/testing'; +import { MemoryRouter, Route, Routes } from 'react-router'; +import Assistants from './Assistants'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { MOCKS } from 'mocks/Assistants'; +import * as Notification from 'common/notification'; + +const notificationSpy = vi.spyOn(Notification, 'setNotification'); +const mockedUsedNavigate = vi.fn(); +vi.mock('react-router-dom', async () => ({ + ...(await vi.importActual('react-router-dom')), + useNavigate: () => mockedUsedNavigate, +})); + +const assistantsComponent = ( + + + + } /> + } /> + + + +); + +test('it renders the list properly and switches between items', async () => { + render(assistantsComponent); + + await waitFor(() => { + expect(screen.getByText('Assistants')).toBeInTheDocument(); + expect(screen.getByText('Assistant-405db438')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.getByText('Instructions')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.getAllByTestId('listItem')).toHaveLength(3); + }); + + fireEvent.click(screen.getAllByTestId('listItem')[1]); +}); + +test('it creates an assistant', async () => { + render(assistantsComponent); + + await waitFor(() => { + expect(screen.getByText('Assistants')).toBeInTheDocument(); + expect(screen.getByText('Assistant-405db438')).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByTestId('headingButton')); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalled(); + }); +}); diff --git a/src/containers/Assistants/Assistants.tsx b/src/containers/Assistants/Assistants.tsx index e58728988..c839acca0 100644 --- a/src/containers/Assistants/Assistants.tsx +++ b/src/containers/Assistants/Assistants.tsx @@ -1,5 +1,5 @@ import { useMutation } from '@apollo/client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { assistantsInfo } from 'common/HelpData'; @@ -11,10 +11,13 @@ import { GET_ASSISTANTS } from 'graphql/queries/Assistant'; import { CreateAssistant } from './CreateAssistant/CreateAssistant'; import { List } from './ListItems/List'; import styles from './Assistants.module.css'; +import { useParams } from 'react-router'; +import { setNotification } from 'common/notification'; export const Assistants = () => { + const params = useParams(); const [updateList, setUpdateList] = useState(false); - const [assistantId, setAssistantId] = useState(null); + const [assistantId, setAssistantId] = useState(null); const [createAssistant] = useMutation(CREATE_ASSISTANT); @@ -26,12 +29,17 @@ export const Assistants = () => { }, }, onCompleted: ({ createAssistant }) => { + setNotification('Assistant created successfully', 'success'); setAssistantId(createAssistant.assistant.id); setUpdateList(!updateList); }, }); }; + useEffect(() => { + if (params.assistantId) setAssistantId(params.assistantId); + }, [params]); + return (
{ @@ -216,6 +216,9 @@ export const CreateAssistant = ({ if (loading || listLoading) { return ; } + + if (!data?.assistant?.assistant) return; + return (
diff --git a/src/containers/Assistants/ListItems/List.tsx b/src/containers/Assistants/ListItems/List.tsx index bdceda26f..93b6cc503 100644 --- a/src/containers/Assistants/ListItems/List.tsx +++ b/src/containers/Assistants/ListItems/List.tsx @@ -9,6 +9,7 @@ import { copyToClipboard } from 'common/utils'; import SearchBar from 'components/UI/SearchBar/SearchBar'; import styles from './List.module.css'; +import { useNavigate } from 'react-router'; interface ListProps { icon?: any; @@ -27,6 +28,7 @@ export const List = ({ setCurrentId, currentId, }: ListProps) => { + const navigate = useNavigate(); const [searchTerm, setSearchTerm] = useState(''); const [showLoadMore, setLoadMore] = useState(false); @@ -103,7 +105,7 @@ export const List = ({
setCurrentId(item.id)} + onClick={() => navigate(`/assistants/${item.id}`)} data-testid="listItem" > {icon &&
{icon}
} diff --git a/src/mocks/Assistants.ts b/src/mocks/Assistants.ts new file mode 100644 index 000000000..36a3e3eb8 --- /dev/null +++ b/src/mocks/Assistants.ts @@ -0,0 +1,151 @@ +import { CREATE_ASSISTANT } from 'graphql/mutations/Assistant'; +import { + GET_ASSISTANT, + GET_ASSISTANTS, + GET_ASSISTANT_FILES, + GET_MODELS, +} from 'graphql/queries/Assistant'; + +const getAssistantsList = { + request: { + query: GET_ASSISTANTS, + variables: { + filter: { + name: '', + }, + opts: { + limit: 25, + order: 'DESC', + }, + }, + }, + result: { + data: { + assistants: [ + { + __typename: 'Assistant', + id: '1', + insertedAt: '2024-10-16T15:58:26Z', + itemId: 'asst_UaWOAyI61Njf9l77Ey9iv0VI', + name: 'Assistant-405db438', + }, + { + __typename: 'Assistant', + id: '2', + insertedAt: '2024-10-16T04:54:20Z', + itemId: 'asst_yK6kJSsdhsoNHwV6o3lr7dty', + name: 'Assistan', + }, + { + __typename: 'Assistant', + id: '3', + insertedAt: '2024-10-15T11:29:28Z', + itemId: 'asst_JhYmNWzpCVBZY2vTuohvmqjs', + name: 'cc4d824d', + }, + ], + }, + }, +}; + +const listOpenaiModels = { + request: { + query: GET_MODELS, + }, + result: { + data: { + listOpenaiModels: ['gpt-4-turbo', 'gpt-4-turbo-2024-04-09', 'chatgpt-4o-latest', 'gpt-4o'], + }, + }, +}; + +const createAssistant = { + request: { + query: CREATE_ASSISTANT, + variables: { + input: { + name: null, + }, + }, + }, + result: { + data: { + createAssistant: { + assistant: { + id: 4, + name: 'Assistant-405db438', + }, + }, + }, + }, +}; + +const getAssistant = (assistantId: any) => ({ + request: { + query: GET_ASSISTANT, + variables: { assistantId }, + }, + result: { + data: { + assistant: { + __typename: 'AssistantResult', + assistant: { + assistantId: 'asst_JhYmNWzpCVBZY2vTuohvmqjs', + id: assistantId, + insertedAt: '2024-10-15T11:29:28Z', + instructions: null, + model: 'gpt-4o', + name: 'cc4d824d', + temperature: 1, + updatedAt: '2024-10-16T15:39:47Z', + }, + }, + }, + }, +}); + +const getAssistantFiles = (assistantId: any) => ({ + request: { + query: GET_ASSISTANT_FILES, + variables: { assistantId }, + }, + result: { + data: { + assistant: { + __typename: 'AssistantResult', + assistant: { + __typename: 'Assistant', + vectorStore: { + __typename: 'VectorStore', + files: [ + { + __typename: 'FileInfo', + fileId: 'file-rls90OGDUgFeLewh6e01Eamf', + filename: 'Accelerator Guide (1).pdf', + }, + ], + id: assistantId, + name: 'VectorStore-77ae3597', + vectorStoreId: 'vs_laIycGtun7qEl0U7zlVsygmy', + }, + }, + }, + }, + }, +}); + +export const MOCKS = [ + getAssistantsList, + getAssistantsList, + createAssistant, + listOpenaiModels, + getAssistant('1'), + getAssistant('1'), + getAssistant('2'), + getAssistant('2'), + getAssistant(4), + getAssistantFiles(4), + getAssistantFiles('1'), + getAssistantFiles(4), + getAssistantFiles('2'), +]; diff --git a/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx b/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx index c750a2e16..dedb1c7e8 100644 --- a/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx +++ b/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx @@ -158,6 +158,7 @@ const routeAdmin = ( } /> } /> + } /> } /> From 4de7a4d728284a83c522b4e6c4a8537555ded94b Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Mon, 28 Oct 2024 10:09:15 +0530 Subject: [PATCH 07/18] refactoring --- src/components/UI/Heading/Heading.tsx | 2 + .../Navigation/SideMenus/SideMenus.module.css | 21 ++++++++++ .../AssistantOptions/AssistantOptions.tsx | 40 ++++--------------- src/containers/Assistants/Assistants.tsx | 14 +++++-- 4 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/components/UI/Heading/Heading.tsx b/src/components/UI/Heading/Heading.tsx index 90efed32b..631402922 100644 --- a/src/components/UI/Heading/Heading.tsx +++ b/src/components/UI/Heading/Heading.tsx @@ -16,6 +16,7 @@ export interface HeadingProps { label: string; action: any; icon?: any; + loading?: boolean; }; } @@ -54,6 +55,7 @@ export const Heading = ({ variant="contained" onClick={() => button.action && button.action()} data-testid="headingButton" + loading={button.loading} > {button.icon || addIcon} {button.label} diff --git a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css index 48cfcb751..eabad80bf 100644 --- a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css +++ b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css @@ -111,4 +111,25 @@ border-radius: 4px; padding: 2px 6px; color: #fff; + animation: jiggle 0.5s ease-in-out; +} + +@keyframes jiggle { + + 0%, + 100% { + transform: rotate(0deg); + } + + 25% { + transform: rotate(-5deg); + } + + 50% { + transform: rotate(5deg); + } + + 75% { + transform: rotate(-5deg); + } } \ No newline at end of file diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index e59809290..3e40cc1a8 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -18,7 +18,7 @@ import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; import { DialogBox } from 'components/UI/DialogBox/DialogBox'; import HelpIcon from 'components/UI/HelpIcon/HelpIcon'; -import { setNotification } from 'common/notification'; +import { setErrorMessage, setNotification } from 'common/notification'; import { ADD_FILES_TO_FILE_SEARCH, @@ -31,7 +31,6 @@ import { GET_ASSISTANT_FILES } from 'graphql/queries/Assistant'; import styles from './AssistantOptions.module.css'; interface AssistantOptionsProps { options: any; - fileSearch: boolean; currentId: any; setOptions: any; } @@ -39,9 +38,6 @@ interface AssistantOptionsProps { const temperatureInfo = 'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.'; -const fileSearchInfo = - 'File search enables the assistant with knowledge from files that you or your users upload. Once a file is uploaded, the assistant automatically decides when to retrieve content based on user requests.'; - export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOptionsProps) => { const [showUploadDialog, setShowUploadDialog] = useState(false); const [files, setFiles] = useState([]); @@ -103,6 +99,9 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp setShowUploadDialog(false); refetch(); }, + onError: (error) => { + setErrorMessage(error); + }, }); } else { setShowUploadDialog(false); @@ -173,35 +172,10 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp return (
- - Tools -
- { - setOptions({ - ...options, - fileSearch: event.target.checked, - }); - }} - /> - } - label={ -
- File Search - -
- } - /> - + + Files + -
diff --git a/src/containers/Assistants/ListItems/List.tsx b/src/containers/Assistants/ListItems/List.tsx index bd8a2a8fe..3f9c9155a 100644 --- a/src/containers/Assistants/ListItems/List.tsx +++ b/src/containers/Assistants/ListItems/List.tsx @@ -12,7 +12,6 @@ import styles from './List.module.css'; import { useNavigate } from 'react-router'; interface ListProps { - icon?: any; getItemsQuery: DocumentNode; listItemName: string; currentId?: any; @@ -21,7 +20,6 @@ interface ListProps { } export const List = ({ - icon, getItemsQuery, listItemName, refreshList, @@ -109,7 +107,6 @@ export const List = ({ onClick={() => navigate(`/assistants/${item.id}`)} data-testid="listItem" > - {icon &&
{icon}
}
{item.name} diff --git a/src/mocks/Assistants.ts b/src/mocks/Assistants.ts index 36a3e3eb8..bbf2245fb 100644 --- a/src/mocks/Assistants.ts +++ b/src/mocks/Assistants.ts @@ -1,4 +1,10 @@ -import { CREATE_ASSISTANT } from 'graphql/mutations/Assistant'; +import { + ADD_FILES_TO_FILE_SEARCH, + CREATE_ASSISTANT, + REMOVE_FILES_FROM_ASSISTANT, + UPDATE_ASSISTANT, + UPLOAD_FILE_TO_OPENAI, +} from 'graphql/mutations/Assistant'; import { GET_ASSISTANT, GET_ASSISTANTS, @@ -6,7 +12,7 @@ import { GET_MODELS, } from 'graphql/queries/Assistant'; -const getAssistantsList = { +const getAssistantsList = (limit: number = 3) => ({ request: { query: GET_ASSISTANTS, variables: { @@ -21,32 +27,15 @@ const getAssistantsList = { }, result: { data: { - assistants: [ - { - __typename: 'Assistant', - id: '1', - insertedAt: '2024-10-16T15:58:26Z', - itemId: 'asst_UaWOAyI61Njf9l77Ey9iv0VI', - name: 'Assistant-405db438', - }, - { - __typename: 'Assistant', - id: '2', - insertedAt: '2024-10-16T04:54:20Z', - itemId: 'asst_yK6kJSsdhsoNHwV6o3lr7dty', - name: 'Assistan', - }, - { - __typename: 'Assistant', - id: '3', - insertedAt: '2024-10-15T11:29:28Z', - itemId: 'asst_JhYmNWzpCVBZY2vTuohvmqjs', - name: 'cc4d824d', - }, - ], + assistants: new Array(limit).fill(null).map((val, ind) => ({ + id: `${ind + 1}`, + insertedAt: '2024-10-16T15:58:26Z', + itemId: 'asst_UaWOAyI61Njf9l77Ey9iv0VI', + name: `Assistant-${ind + 1}`, + })), }, }, -}; +}); const listOpenaiModels = { request: { @@ -134,18 +123,154 @@ const getAssistantFiles = (assistantId: any) => ({ }, }); +const loadMoreQuery = { + request: { + query: GET_ASSISTANTS, + variables: { filter: { name: '' }, opts: { limit: 50, offset: 25, order: 'DESC' } }, + }, + result: { + data: { + assistants: [], + }, + }, +}; + +const getAssistantListOnSearch = { + request: { + query: GET_ASSISTANTS, + variables: { filter: { name: 'testAssistant' }, opts: { limit: 25, order: 'DESC' } }, + }, + result: { + data: { + assistants: [ + { + id: `2`, + insertedAt: '2024-10-16T15:58:26Z', + itemId: 'asst_UaWOAyI61Njf9l77Ey9iv0VI', + name: `testAssistant`, + }, + ], + }, + }, +}; + +const uploadFileToFileSearch = { + request: { + query: UPLOAD_FILE_TO_OPENAI, + }, + result: { + data: { + uploadFilesearchFile: { + fileId: 'file-rls90OGDUgFeLewh6e01Eamf', + filename: 'Accelerator Guide (1).pdf', + }, + }, + }, + variableMatcher: (variables: any) => true, +}; + +const removeFileFromAssistant = { + request: { + query: REMOVE_FILES_FROM_ASSISTANT, + variables: { fileId: 'file-rls90OGDUgFeLewh6e01Eamf', removeAssistantFileId: '1' }, + }, + result: { + data: { + removeAssistantFile: { + assistant: { + id: '1', + }, + errors: null, + }, + }, + }, +}; + +const addFilesToFilesearch = { + request: { + query: ADD_FILES_TO_FILE_SEARCH, + variables: { + addAssistantFilesId: '1', + mediaInfo: [ + { fileId: 'file-rls90OGDUgFeLewh6e01Eamf', filename: 'Accelerator Guide (1).pdf' }, + { fileId: 'file-rls90OGDUgFeLewh6e01Eamf', filename: 'Accelerator Guide (1).pdf' }, + ], + }, + }, + result: { + data: { + addAssistantFiles: { + assistant: { + id: '1', + }, + errors: null, + }, + }, + }, +}; + +const updateAssistant = { + request: { + query: UPDATE_ASSISTANT, + variables: { + updateAssistantId: '1', + input: { + instructions: 'test instructions', + model: 'chatgpt-4o-latest', + name: 'test name', + temperature: '1.5', + }, + }, + }, + result: { + data: { + updateAssistant: { + assistant: { + id: '1', + name: 'test name', + }, + errors: null, + }, + }, + }, +}; + +const uploadFileMocks = [ + uploadFileToFileSearch, + uploadFileToFileSearch, + uploadFileToFileSearch, + removeFileFromAssistant, + addFilesToFilesearch, +]; + export const MOCKS = [ - getAssistantsList, - getAssistantsList, + getAssistantsList(), + getAssistantsList(), createAssistant, listOpenaiModels, getAssistant('1'), getAssistant('1'), getAssistant('2'), getAssistant('2'), - getAssistant(4), - getAssistantFiles(4), + getAssistant('2'), + getAssistant('4'), + getAssistantFiles('4'), + getAssistantFiles('1'), + getAssistantFiles('1'), getAssistantFiles('1'), getAssistantFiles(4), getAssistantFiles('2'), + getAssistantFiles('2'), + getAssistantListOnSearch, + updateAssistant, + ...uploadFileMocks, +]; + +export const emptyMocks = [getAssistantsList(0), listOpenaiModels, getAssistant('2')]; + +export const loadMoreMocks = [ + getAssistantsList(25), + listOpenaiModels, + loadMoreQuery, + getAssistant('1'), ]; From 5ae1792ff17cbe2cf8c6aed125c43ad205efb752 Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Mon, 4 Nov 2024 22:06:32 +0530 Subject: [PATCH 11/18] added test cases --- src/containers/Assistants/Assistants.test.tsx | 30 +++++++++++++++++++ .../CreateAssistant/CreateAssistant.tsx | 4 +-- src/mocks/Assistants.ts | 22 ++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/containers/Assistants/Assistants.test.tsx b/src/containers/Assistants/Assistants.test.tsx index 2156731d1..b5f6b8ba0 100644 --- a/src/containers/Assistants/Assistants.test.tsx +++ b/src/containers/Assistants/Assistants.test.tsx @@ -168,6 +168,8 @@ test('it updates the assistant', async () => { expect(screen.getByText('Instructions')).toBeInTheDocument(); }); + fireEvent.click(screen.getByTestId('copyCurrentAssistantId')); + const autocompletes = screen.getAllByTestId('AutocompleteInput'); const inputs = screen.getAllByRole('textbox'); @@ -186,3 +188,31 @@ test('it updates the assistant', async () => { expect(notificationSpy).toHaveBeenCalled(); }); }); + +test('it deletes the assistant', async () => { + render(assistantsComponent()); + + await waitFor(() => { + expect(screen.getByText('Assistants')).toBeInTheDocument(); + expect(screen.getByText('Assistant-1')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.getByText('Instructions')).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByTestId('removeAssistant')); + fireEvent.click(screen.getByTestId('cancel-button')); + + fireEvent.click(screen.getByTestId('removeAssistant')); + + await waitFor(() => { + expect(screen.getByTestId('dialogBox')).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByTestId('ok-button')); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalled(); + }); +}); diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index 027d42305..fbdf5023b 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -136,7 +136,7 @@ export const CreateAssistant = ({ helperText: (
Give a recognizable name for your assistant -
copyToClipboard(assistantId)}> +
copyToClipboard(assistantId)}> {assistantId}
@@ -247,7 +247,7 @@ export const CreateAssistant = ({ onClick={() => setShowConfirmation(true)} variant="outlined" color="error" - data-testid="cancelAction" + data-testid="removeAssistant" > Remove diff --git a/src/mocks/Assistants.ts b/src/mocks/Assistants.ts index bbf2245fb..823b14e05 100644 --- a/src/mocks/Assistants.ts +++ b/src/mocks/Assistants.ts @@ -1,6 +1,7 @@ import { ADD_FILES_TO_FILE_SEARCH, CREATE_ASSISTANT, + DELETE_ASSISTANT, REMOVE_FILES_FROM_ASSISTANT, UPDATE_ASSISTANT, UPLOAD_FILE_TO_OPENAI, @@ -235,6 +236,26 @@ const updateAssistant = { }, }; +const removeAssistant = { + request: { + query: DELETE_ASSISTANT, + variables: { + deleteAssistantId: '1', + }, + }, + result: { + data: { + deleteAssistant: { + assistant: { + assistantId: '1', + name: 'test name', + }, + errors: null, + }, + }, + }, +}; + const uploadFileMocks = [ uploadFileToFileSearch, uploadFileToFileSearch, @@ -263,6 +284,7 @@ export const MOCKS = [ getAssistantFiles('2'), getAssistantListOnSearch, updateAssistant, + removeAssistant, ...uploadFileMocks, ]; From de1b4ccbd7a6b76de5ae7e9867314e58570a8d52 Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Tue, 5 Nov 2024 10:08:41 +0530 Subject: [PATCH 12/18] minor refactoring --- .../Assistants/AssistantOptions/AssistantOptions.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 8b9788b66..92b575fbb 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -41,8 +41,10 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp const { refetch, data } = useQuery(GET_ASSISTANT_FILES, { variables: { assistantId: currentId }, onCompleted: ({ assistant }) => { - const attachedFiles = assistant.assistant.vectorStore.files; - setFiles([...files, ...attachedFiles.map((item: any) => ({ ...item, attached: true }))]); + if (assistant.assistant.vectorStore) { + const attachedFiles = assistant.assistant.vectorStore.files; + setFiles([...files, ...attachedFiles.map((item: any) => ({ ...item, attached: true }))]); + } }, }); From ed4ed2ebbcc4875f93c3b9a43e8691482b18c61d Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Wed, 6 Nov 2024 13:26:56 +0530 Subject: [PATCH 13/18] added localizations + resolved comments --- .../AssistantOptions/AssistantOptions.tsx | 12 +++++----- src/containers/Assistants/Assistants.tsx | 12 +++++----- .../CreateAssistant/CreateAssistant.tsx | 22 +++++++++++-------- .../Assistants/ListItems/List.module.css | 1 + src/containers/Assistants/ListItems/List.tsx | 4 +++- src/i18n/en/en.json | 20 ++++++++++++++++- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 92b575fbb..da4232a34 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -1,6 +1,7 @@ import { useMutation, useQuery } from '@apollo/client'; import { Button, CircularProgress, IconButton, Slider, Typography } from '@mui/material'; import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import AddIcon from 'assets/images/AddGreenIcon.svg?react'; import DatabaseIcon from 'assets/images/database.svg?react'; @@ -33,6 +34,7 @@ const temperatureInfo = export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOptionsProps) => { const [showUploadDialog, setShowUploadDialog] = useState(false); const [files, setFiles] = useState([]); + const { t } = useTranslation(); const [uploadFile, { loading: uploadingFile }] = useMutation(UPLOAD_FILE_TO_OPENAI); const [addFilesToFileSearch, { loading: addingFiles }] = useMutation(ADD_FILES_TO_FILE_SEARCH); @@ -112,7 +114,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp dialog = ( setShowUploadDialog(false)} buttonOk="Add" fullWidth @@ -133,7 +135,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp ) : ( <> - Upload File + {t('Upload File')} )} Max File Size: 20MB - Information in the attached files will be available to this assistant. + {t('Information in the attached files will be available to this assistant.')} Learn More
@@ -183,7 +185,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp variant="outlined" > - Add Files + {t('Add Files')}
{data?.assistant.assistant.vectorStore && ( @@ -204,7 +206,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp
- Temperature + {t('Temperature')} { const [updateList, setUpdateList] = useState(false); const [assistantId, setAssistantId] = useState(null); const navigate = useNavigate(); + const { t } = useTranslation(); const [createAssistant, { loading }] = useMutation(CREATE_ASSISTANT); @@ -30,7 +32,7 @@ export const Assistants = () => { }, }, onCompleted: ({ createAssistant }) => { - setNotification('Assistant created successfully', 'success'); + setNotification(t('Assistant created successfully'), 'success'); navigate(`/assistants/${createAssistant.assistant.id}`); setUpdateList(!updateList); }, @@ -44,12 +46,12 @@ export const Assistants = () => { return (
{ updateList={updateList} /> ) : ( -

Select/Create an assistant

+

{t('Select/Create an assistant')}

)}
diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index fbdf5023b..09f91876a 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -2,6 +2,7 @@ import { useLazyQuery, useMutation, useQuery } from '@apollo/client'; import { Typography } from '@mui/material'; import { Field, FormikProvider, useFormik } from 'formik'; import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import * as Yup from 'yup'; import { copyToClipboard } from 'common/utils'; @@ -41,6 +42,7 @@ export const CreateAssistant = ({ const [instructions, setInstructions] = useState(''); const [options, setOptions] = useState({ fileSearch: true, temperature: 1 }); const [showConfirmation, setShowConfirmation] = useState(false); + const { t } = useTranslation(); const states = { name, @@ -123,19 +125,21 @@ export const CreateAssistant = ({ options: modelOptions || [], optionLabel: 'label', multiple: false, - label: 'Model', - helperText: 'Choose the best model for your needs.', + label: t('Model'), + helperText: t('Choose the best model for your needs.'), onChange: (value: any) => setModel(value), }, { component: Input, name: 'name', type: 'text', - label: 'Name', + label: t('Name'), onChange: (value: any) => setName(value), helperText: (
- Give a recognizable name for your assistant + + {t('Give a recognizable name for your assistant')} +
copyToClipboard(assistantId)}> {assistantId} @@ -147,10 +151,10 @@ export const CreateAssistant = ({ component: Input, name: 'instructions', type: 'text', - label: 'Instructions', + label: t('Instructions'), rows: 3, textArea: true, - helperText: 'Set the instructions according to your requirements.', + helperText: t('Set the instructions according to your requirements.'), onChange: (value: any) => setInstructions(value), }, { @@ -208,7 +212,7 @@ export const CreateAssistant = ({ buttonOkLoading={deletingAssistant} disableOk={deletingAssistant} > -
You won't be able to use this assistant.
+
{t("You won't be able to use this assistant.")}
); } @@ -241,7 +245,7 @@ export const CreateAssistant = ({ variant="contained" data-testid="submitAction" > - Save Changes + {t('Save Changes')}
diff --git a/src/containers/Assistants/ListItems/List.module.css b/src/containers/Assistants/ListItems/List.module.css index f43da4813..6178623e7 100644 --- a/src/containers/Assistants/ListItems/List.module.css +++ b/src/containers/Assistants/ListItems/List.module.css @@ -13,6 +13,7 @@ flex-direction: column; row-gap: 0.5rem; overflow-y: scroll; + scrollbar-width: none; } .Item { diff --git a/src/containers/Assistants/ListItems/List.tsx b/src/containers/Assistants/ListItems/List.tsx index 3f9c9155a..97163dc63 100644 --- a/src/containers/Assistants/ListItems/List.tsx +++ b/src/containers/Assistants/ListItems/List.tsx @@ -2,6 +2,7 @@ import { DocumentNode, useQuery } from '@apollo/client'; import { IconButton } from '@mui/material'; import dayjs from 'dayjs'; import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import CopyIcon from 'assets/images/icons/Settings/Copy.svg?react'; import { DEFAULT_ENTITY_LIMIT, DEFAULT_MESSAGE_LOADMORE_LIMIT } from 'common/constants'; @@ -29,6 +30,7 @@ export const List = ({ const navigate = useNavigate(); const [searchTerm, setSearchTerm] = useState(''); const [showLoadMore, setLoadMore] = useState(false); + const { t } = useTranslation(); const { data, refetch, fetchMore } = useQuery(getItemsQuery, { variables: { @@ -130,7 +132,7 @@ export const List = ({ ))} {showLoadMore ? ( - Load More + {t('Load more')} ) : null}
diff --git a/src/i18n/en/en.json b/src/i18n/en/en.json index 09ea81d46..977c189dc 100644 --- a/src/i18n/en/en.json +++ b/src/i18n/en/en.json @@ -512,5 +512,23 @@ "Template Flows": "Template Flows", "Template Flow": "Template Flow", "Template flow copy": "Template flow copy", - "Flow created successfully from template!": "Flow created successfully from template!" + "Flow created successfully from template!": "Flow created successfully from template!", + "Select/Create an assistant": "Select/Create an assistant", + "Create Assistant": "Create Assistant", + "Assistants": "Assistants", + "Purpose-built AI that uses OpenAI's models and calls tools": "Purpose-built AI that uses OpenAI's models and calls tools", + "Assistant created successfully": "Assistant created successfully", + "Save Changes": "Save Changes", + "Remove": "Remove", + "You won't be able to use this assistant.": "You won't be able to use this assistant.", + "Model": "Model", + "Instructions": "Instructions", + "Choose the best model for your needs.": "Choose the best model for your needs.", + "Give a recognizable name for your assistant": "Give a recognizable name for your assistant", + "Set the instructions according to your requirements.": "Set the instructions according to your requirements.", + "Information in the attached files will be available to this assistant.": "Information in the attached files will be available to this assistant.", + "Temperature": "Temperature", + "Upload File": "Upload File", + "Add Files": "Add Files", + "Add files to file search": "Add files to file search" } From 1987248164f8bcd665c4a6707efc268e28d6ff40 Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Fri, 8 Nov 2024 10:05:13 +0530 Subject: [PATCH 14/18] minor fixes --- .../Assistants/AssistantOptions/AssistantOptions.tsx | 4 +++- src/containers/Assistants/ListItems/List.module.css | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index da4232a34..4f44c0d02 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -165,7 +165,9 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp {t('Information in the attached files will be available to this assistant.')} - Learn More + + Learn More +
diff --git a/src/containers/Assistants/ListItems/List.module.css b/src/containers/Assistants/ListItems/List.module.css index 6178623e7..d254f850f 100644 --- a/src/containers/Assistants/ListItems/List.module.css +++ b/src/containers/Assistants/ListItems/List.module.css @@ -16,6 +16,10 @@ scrollbar-width: none; } +.ListContainer ::-webkit-scrollbar { + display: none !important; +} + .Item { background-color: #fff; padding: 0.8rem 1rem; From b13495c0f5efcaf0008ee404ca75078f96bde16c Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Fri, 8 Nov 2024 10:09:57 +0530 Subject: [PATCH 15/18] updated animation --- .../Navigation/SideMenus/SideMenus.module.css | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css index eabad80bf..f73610e05 100644 --- a/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css +++ b/src/components/UI/Layout/Navigation/SideMenus/SideMenus.module.css @@ -111,25 +111,38 @@ border-radius: 4px; padding: 2px 6px; color: #fff; - animation: jiggle 0.5s ease-in-out; + animation: animation 1s ease-in-out; } -@keyframes jiggle { +@keyframes animation { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } - 0%, - 100% { - transform: rotate(0deg); + 10%, + 20% { + -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); } - 25% { - transform: rotate(-5deg); + 30%, + 50%, + 70%, + 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.3, 1.3, 1.3) rotate3d(0, 0, 1, 3deg); } - 50% { - transform: rotate(5deg); + 40%, + 60%, + 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); } - 75% { - transform: rotate(-5deg); + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); } } \ No newline at end of file From 25a2e4d7070aa815e0b6edca090003d51ca6948f Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Fri, 8 Nov 2024 13:34:51 +0530 Subject: [PATCH 16/18] removed scrollbar --- src/containers/Assistants/ListItems/List.module.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/containers/Assistants/ListItems/List.module.css b/src/containers/Assistants/ListItems/List.module.css index d254f850f..873f2169d 100644 --- a/src/containers/Assistants/ListItems/List.module.css +++ b/src/containers/Assistants/ListItems/List.module.css @@ -16,7 +16,8 @@ scrollbar-width: none; } -.ListContainer ::-webkit-scrollbar { +.ListContainer ::-webkit-scrollbar, +::-webkit-scrollbar-track { display: none !important; } From 4e09dde8331d93b282c07c5306f5f65d667f8b9a Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Fri, 8 Nov 2024 14:54:25 +0530 Subject: [PATCH 17/18] minor refactoring --- .../CreateAssistant/CreateAssistant.module.css | 4 ++++ .../Assistants/CreateAssistant/CreateAssistant.tsx | 9 ++++++--- src/i18n/en/en.json | 1 - 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css b/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css index fc28d7dd8..98b4da8de 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.module.css @@ -61,4 +61,8 @@ .HelperText { color: #999999; font-weight: normal; +} + +.DeleteIcon { + margin-right: 0.5rem; } \ No newline at end of file diff --git a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx index 09f91876a..063f93e1f 100644 --- a/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx +++ b/src/containers/Assistants/CreateAssistant/CreateAssistant.tsx @@ -18,6 +18,7 @@ import { GET_ASSISTANT, GET_MODELS } from 'graphql/queries/Assistant'; import { DELETE_ASSISTANT, UPDATE_ASSISTANT } from 'graphql/mutations/Assistant'; import CopyIcon from 'assets/images/CopyGreen.svg?react'; +import DeleteIcon from 'assets/images/icons/Delete/White.svg?react'; import { AssistantOptions } from '../AssistantOptions/AssistantOptions'; @@ -245,14 +246,16 @@ export const CreateAssistant = ({ variant="contained" data-testid="submitAction" > - {t('Save Changes')} + {t('Save')}
diff --git a/src/i18n/en/en.json b/src/i18n/en/en.json index 977c189dc..a05addfc7 100644 --- a/src/i18n/en/en.json +++ b/src/i18n/en/en.json @@ -518,7 +518,6 @@ "Assistants": "Assistants", "Purpose-built AI that uses OpenAI's models and calls tools": "Purpose-built AI that uses OpenAI's models and calls tools", "Assistant created successfully": "Assistant created successfully", - "Save Changes": "Save Changes", "Remove": "Remove", "You won't be able to use this assistant.": "You won't be able to use this assistant.", "Model": "Model", From 3d668cf7591a7b40239b91474587fe0b52fc5edc Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Fri, 8 Nov 2024 20:48:30 +0530 Subject: [PATCH 18/18] added warning for unsupported files --- .../AssistantOptions/AssistantOptions.tsx | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx index 4f44c0d02..642a4aea1 100644 --- a/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx +++ b/src/containers/Assistants/AssistantOptions/AssistantOptions.tsx @@ -69,6 +69,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp }; setFiles([...files, uploadFilesearchFile]); }, + onError: (errors) => setErrorMessage(errors), }); }; @@ -123,13 +124,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp buttonOkLoading={addingFiles} >
- {files.length > 0 && ( @@ -181,11 +171,7 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp Files -