Skip to content

Commit

Permalink
Add chatbot name validation
Browse files Browse the repository at this point in the history
Assure it's unique.
  • Loading branch information
rebeccaalpert committed Dec 4, 2024
1 parent 0c715e6 commit dfc7aa2
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 48 deletions.
7 changes: 6 additions & 1 deletion src/app/AppData/AppDataContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ import * as React from 'react';
interface AppDataContextType {
flyoutMenuSelectedChatbot?: CannedChatbot;
updateFlyoutMenuSelectedChatbot: (newChatbot: CannedChatbot) => void;
chatbots: CannedChatbot[];
setChatbots: (chatbots: CannedChatbot[]) => void;
}

const AppDataContext = React.createContext<AppDataContextType | undefined>(undefined);

export const AppDataProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [flyoutMenuSelectedChatbot, setFlyoutMenuSelectedChatbot] = React.useState<CannedChatbot>();
const [chatbots, setChatbots] = React.useState<CannedChatbot[]>([]);

const updateFlyoutMenuSelectedChatbot = (newChatbot: CannedChatbot) =>
setFlyoutMenuSelectedChatbot((prev) => ({ ...prev, ...newChatbot }));

return (
<AppDataContext.Provider value={{ flyoutMenuSelectedChatbot, updateFlyoutMenuSelectedChatbot }}>
<AppDataContext.Provider
value={{ flyoutMenuSelectedChatbot, updateFlyoutMenuSelectedChatbot, chatbots, setChatbots }}
>
{children}
</AppDataContext.Provider>
);
Expand Down
6 changes: 5 additions & 1 deletion src/app/BaseChatbot/BaseChatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useAppData } from '@app/AppData/AppDataContext';

const BaseChatbot: React.FunctionComponent = () => {
const { chatbots } = useLoaderData() as { chatbots: CannedChatbot[] };
const { flyoutMenuSelectedChatbot, updateFlyoutMenuSelectedChatbot } = useAppData();
const { flyoutMenuSelectedChatbot, updateFlyoutMenuSelectedChatbot, setChatbots } = useAppData();
const [isSendButtonDisabled, setIsSendButtonDisabled] = React.useState(true);
const [messages, setMessages] = React.useState<MessageProps[]>([]);
const [currentMessage, setCurrentMessage] = React.useState<string[]>([]);
Expand All @@ -48,6 +48,10 @@ const BaseChatbot: React.FunctionComponent = () => {
document.title = `Red Hat Composer AI Studio | ${currentChatbot?.name}`;
}, []);

Check warning on line 49 in src/app/BaseChatbot/BaseChatbot.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook React.useEffect has a missing dependency: 'currentChatbot?.name'. Either include it or remove the dependency array

React.useEffect(() => {
setChatbots(chatbots);
}, [chatbots]);

Check warning on line 53 in src/app/BaseChatbot/BaseChatbot.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook React.useEffect has a missing dependency: 'setChatbots'. Either include it or remove the dependency array

React.useEffect(() => {
document.title = `Red Hat Composer AI Studio | ${currentChatbot?.name}`;
}, [currentChatbot]);
Expand Down
8 changes: 8 additions & 0 deletions src/app/Compare/CompareLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useChildStatus } from './ChildStatusProvider';
import { ErrorObject } from '@app/types/ErrorObject';
import { UserFacingFile } from '@app/types/UserFacingFile';
import { getId } from '@app/utils/utils';
import { useAppData } from '@app/AppData/AppDataContext';

export const CompareLayout: React.FunctionComponent = () => {
// information from api
Expand Down Expand Up @@ -38,6 +39,13 @@ export const CompareLayout: React.FunctionComponent = () => {
// context, used for stop buttons
const { status } = useChildStatus();

// context, used for panels
const { setChatbots } = useAppData();

React.useEffect(() => {
setChatbots(chatbots);
}, [chatbots]);

Check warning on line 47 in src/app/Compare/CompareLayout.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook React.useEffect has a missing dependency: 'setChatbots'. Either include it or remove the dependency array

React.useEffect(() => {
document.title = `Red Hat Composer AI Studio | Compare`;
if (assistants && assistants.length === 2) {
Expand Down
31 changes: 28 additions & 3 deletions src/app/FlyoutForm/FlyoutForm.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useAppData } from '@app/AppData/AppDataContext';
import { FlyoutError } from '@app/FlyoutError/FlyoutError';
import { FlyoutFooter } from '@app/FlyoutFooter/FlyoutFooter';
import { FlyoutHeader } from '@app/FlyoutHeader.tsx/FlyoutHeader';
Expand All @@ -19,6 +20,7 @@ import {
TextArea,
TextInput,
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import * as React from 'react';

interface RetrieverAPIResponse {
Expand All @@ -38,6 +40,8 @@ interface FlyoutFormProps {
hideFlyout: () => void;
}

type validate = 'success' | 'error' | 'default';

export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, hideFlyout }: FlyoutFormProps) => {
const [isLoading, setIsLoading] = React.useState(true);
const [loadedFormFields, setLoadedFormFields] = React.useState(false);
Expand All @@ -50,9 +54,11 @@ export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, h
const [llms, setLLMs] = React.useState<LLMAPIResponse[]>([]);
const [isRetrieverDropdownOpen, setIsRetrieverDropdownOpen] = React.useState(false);
const [isLLMDropdownOpen, setIsLLMDropdownOpen] = React.useState(false);
const [validated, setValidated] = React.useState<validate>('default');
const [selectedLLM, setSelectedLLM] = React.useState<LLMAPIResponse>();
const [prompt, setPrompt] = React.useState<string>();
const { nextStep, prevStep } = useFlyoutWizard();
const { chatbots } = useAppData();

const ERROR_BODY = {
'Error: 404': `Service is currently unavailable. Click retry or try again later.`,
Expand Down Expand Up @@ -128,8 +134,19 @@ export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, h
loadData();
}, []);

Check warning on line 135 in src/app/FlyoutForm/FlyoutForm.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook React.useEffect has a missing dependency: 'loadData'. Either include it or remove the dependency array

const chatbotExists = (title: string) => {
return chatbots.filter((chatbot) => chatbot.name === title).length >= 1;
};

const handleTitleChange = (_event, title: string) => {
setTitle(title);
if (title.trim() === '') {
setValidated('default');
} else if (!chatbotExists(title)) {
setValidated('success');
} else {
setValidated('error');
}
};

const handleDisplayNameChange = (_event, name: string) => {
Expand Down Expand Up @@ -175,6 +192,9 @@ export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, h
};

try {
if (title === '' && !selectedLLM && validated !== 'success') {
throw new Error('Missing form fields');
}
const response = await fetch(url, {
method: 'POST',
headers: {
Expand All @@ -201,7 +221,7 @@ export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, h

const onClick = async () => {
setError(undefined);
if (loadedFormFields && title !== '' && selectedLLM) {
if (loadedFormFields && title !== '' && selectedLLM && validated === 'success') {
const data = await createAssistant();
if (data) {
nextStep();
Expand Down Expand Up @@ -231,7 +251,12 @@ export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, h
/>
<FormHelperText>
<HelperText>
<HelperTextItem>Unique name for your assistant</HelperTextItem>
<HelperTextItem
icon={validated === 'error' ? <ExclamationCircleIcon /> : undefined}
variant={validated}
>
{validated === 'error' ? 'Must be unique' : 'Unique name for your assistant'}
</HelperTextItem>
</HelperText>
</FormHelperText>
</FormGroup>
Expand Down Expand Up @@ -347,7 +372,7 @@ export const FlyoutForm: React.FunctionComponent<FlyoutFormProps> = ({ header, h
</div>
{!error && (
<FlyoutFooter
isPrimaryButtonDisabled={title === '' || !selectedLLM}
isPrimaryButtonDisabled={title === '' || !selectedLLM || validated !== 'success'}
primaryButton="Create assistant"
onPrimaryButtonClick={onClick}
secondaryButton="Cancel"
Expand Down
48 changes: 5 additions & 43 deletions src/app/FlyoutList/FlyoutList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { FlyoutLoading } from '@app/FlyoutLoading/FlyoutLoading';
import { useFlyoutWizard } from '@app/FlyoutWizard/FlyoutWizardContext';
import { CannedChatbot } from '@app/types/CannedChatbot';
import { ErrorObject } from '@app/types/ErrorObject';
import { ERROR_TITLE } from '@app/utils/utils';
import { Label, Menu, MenuContent, MenuItem, MenuList, SearchInput } from '@patternfly/react-core';
import * as React from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
Expand All @@ -32,30 +31,7 @@ export const FlyoutList: React.FunctionComponent<FlyoutListProps> = ({
const { nextStep } = useFlyoutWizard();
const location = useLocation();
const navigate = useNavigate();
const { flyoutMenuSelectedChatbot, updateFlyoutMenuSelectedChatbot } = useAppData();

const ERROR_BODY = {
'Error: 404': `Service is currently unavailable. Click retry or try again later.`,
'Error: 500': `Service has encountered an error. Click retry or try again later.`,
'Error: Other': `Service has encountered an error. Click retry or try again later.`,
};

const handleError = (e) => {
console.error(e);
const title = ERROR_TITLE[e];
const body = ERROR_BODY[e];
let newError;
if (title && body) {
newError = { title: ERROR_TITLE[e], body: ERROR_BODY[e] };
} else {
if ('message' in e) {
newError = { title: 'Error', body: e.message };
} else {
newError = { title: 'Error', body: e };
}
}
setError(newError);
};
const { flyoutMenuSelectedChatbot, updateFlyoutMenuSelectedChatbot, chatbots } = useAppData();

const header = (
<div className="title-with-label">
Expand All @@ -64,24 +40,9 @@ export const FlyoutList: React.FunctionComponent<FlyoutListProps> = ({
);

const getAssistants = async () => {
const url = process.env.REACT_APP_INFO_URL ?? '';

try {
const response = await fetch(url);

if (!response.ok) {
throw new Error(`${response.status}`);
}

const data = await response.json();
setItems(data);
setFilteredItems(data);
setIsLoading(false);
} catch (error) {
console.error('Error loading assistants', error);
handleError(error);
setIsLoading(false);
}
setItems(chatbots);
setFilteredItems(chatbots);
setIsLoading(false);
};

const loadAssistants = async () => {
Expand All @@ -101,6 +62,7 @@ export const FlyoutList: React.FunctionComponent<FlyoutListProps> = ({
itemId={item.name}
key={item.name}
isSelected={item.name === flyoutMenuSelectedChatbot?.name}
description={item.description}
>
{item.displayName ?? item.name}
</MenuItem>
Expand Down

0 comments on commit dfc7aa2

Please sign in to comment.