Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.FormLayout {
width: 800px;
}

.GoldenQAHelper {
margin-top: 8px;
}
Expand Down Expand Up @@ -47,11 +43,16 @@

.GoldenQaRow {
display: flex;
align-items: flex-start;
align-items: center;
gap: 12px;
}

.UploadGoldenQaButton {
margin-top: 40px;
white-space: nowrap;
border-radius: 20px !important;
flex-shrink: 0;
}

.GoldenQaHelperText {
margin-top: 6px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
createGoldenQaCustomSuccessMock,
createGoldenQaSuccessMock,
getAIEvaluationCreateMocks,
getAssistantConfigVersionsMock,
} from 'mocks/AIEvaluations';
import AIEvaluationCreate, { DUMMY_CREATE, DUMMY_GET_ITEM } from './AIEvaluationCreate';
import AIEvaluationCreate from './AIEvaluationCreate';

const defaultMocks = getAIEvaluationCreateMocks(DUMMY_GET_ITEM, DUMMY_CREATE);
const defaultMocks = getAIEvaluationCreateMocks();

const wrapper = (mocks: any[] = defaultMocks) => (
<MockedProvider mocks={mocks}>
Expand Down Expand Up @@ -67,12 +68,12 @@ describe('AIEvaluationCreate', () => {
});

expect(
screen.getByText(/Select the Golden QA dataset from the existing list or upload a new set/)
screen.getByText(/Select The Golden QA Dataset From Existing List Or Upload A New Golden Set/)
).toBeInTheDocument();
expect(screen.getByText('Expected CSV Format:')).toBeInTheDocument();
expect(screen.getByText('Question,Answer')).toBeInTheDocument();
expect(screen.getByText('What is the capital of France?,Paris')).toBeInTheDocument();
expect(screen.getByText('Click here for the template CSV')).toBeInTheDocument();
expect(screen.getByText('Question, Answer')).toBeInTheDocument();
expect(screen.getByText('{"What Is X"},{"Answer"}')).toBeInTheDocument();
expect(screen.getByText('Click Here For The Template Csv')).toBeInTheDocument();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@priyanshu6238 why do these values keep changing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i change it according to the noopur design

});

test('renders Upload Golden QA button', async () => {
Expand Down Expand Up @@ -150,10 +151,10 @@ describe('AIEvaluationCreate', () => {
render(wrapper());

await waitFor(() => {
expect(screen.getByPlaceholderText('Give a unique name for the evaluation experiment.')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Give a unique name for the evaluation experiment')).toBeInTheDocument();
});

const nameInput = screen.getByPlaceholderText('Give a unique name for the evaluation experiment.');
const nameInput = screen.getByPlaceholderText('Give a unique name for the evaluation experiment');
fireEvent.change(nameInput, { target: { value: 'valid_name' } });
fireEvent.click(screen.getByText('Run Evaluation'));

Expand All @@ -166,10 +167,10 @@ describe('AIEvaluationCreate', () => {
render(wrapper());

await waitFor(() => {
expect(screen.getByPlaceholderText('Give a unique name for the evaluation experiment.')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Give a unique name for the evaluation experiment')).toBeInTheDocument();
});

const nameInput = screen.getByPlaceholderText('Give a unique name for the evaluation experiment.');
const nameInput = screen.getByPlaceholderText('Give a unique name for the evaluation experiment');
fireEvent.change(nameInput, { target: { value: 'invalid name with spaces' } });
fireEvent.click(screen.getByText('Run Evaluation'));

Expand All @@ -182,15 +183,34 @@ describe('AIEvaluationCreate', () => {
render(wrapper());

await waitFor(() => {
expect(screen.getByPlaceholderText('Give a unique name for the evaluation experiment.')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Give a unique name for the evaluation experiment')).toBeInTheDocument();
});

const nameInput = screen.getByPlaceholderText('Give a unique name for the evaluation experiment.');
const nameInput = screen.getByPlaceholderText('Give a unique name for the evaluation experiment');
fireEvent.change(nameInput, { target: { value: 'valid_evaluation-name123' } });

expect((nameInput as HTMLInputElement).value).toBe('valid_evaluation-name123');
});

test('shows assistant options from query using assistantName and versionNumber', async () => {
render(wrapper([...defaultMocks, getAssistantConfigVersionsMock]));

await waitFor(() => {
expect(screen.getByText('Create AI Evaluation')).toBeInTheDocument();
});

const dropdowns = screen.getAllByTestId('dropdown');
const assistantDropdown = dropdowns[1];
const selectTrigger =
assistantDropdown.querySelector('[role="combobox"]') ?? assistantDropdown.querySelector('button');
fireEvent.mouseDown(selectTrigger!);

await waitFor(() => {
expect(screen.getByText('Test Assistant (Version 2)')).toBeInTheDocument();
expect(screen.getByText('Test Assistant (Version 1)')).toBeInTheDocument();
});
});

test('shows assistant helper text', async () => {
render(wrapper());

Expand Down Expand Up @@ -284,8 +304,8 @@ describe('AIEvaluationCreate', () => {
render(
wrapper([
...defaultMocks,
createGoldenQaCustomSuccessMock('first_qa', 1),
createGoldenQaCustomSuccessMock('second_qa', 1),
createGoldenQaCustomSuccessMock('first_qa', 1, '100'),
createGoldenQaCustomSuccessMock('second_qa', 1, '200'),
])
);

Expand Down
145 changes: 89 additions & 56 deletions src/containers/AIEvals/AIEvaluationCreate/AIEvaluationCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,83 +1,93 @@
import { gql } from '@apollo/client';
import { useMutation, useQuery } from '@apollo/client';
import { Button } from 'components/UI/Form/Button/Button';
import { Dropdown } from 'components/UI/Form/Dropdown/Dropdown';
import { Input } from 'components/UI/Form/Input/Input';
import { FormLayout } from 'containers/Form/FormLayout';
import { CREATE_EVALUATION } from 'graphql/mutations/AIEvaluations';
// src / graphql / mutations / AIEvaluations.ts;
import { GET_ASSISTANT_CONFIG_VERSIONS } from 'graphql/queries/Assistant';
import React, { useRef, useState } from 'react';
import { useNavigate } from 'react-router';
import * as Yup from 'yup';
import { setNotification } from 'common/notification';

import styles from './AIEvaluationCreate.module.css';
import { UploadGoldenQaDialog } from './UploadGoldenQaDialog';

// Dummy GraphQL documents until backend supports get/update/delete for AI evaluations (exported for tests)
export const DUMMY_GET_ITEM = gql`
query DummyAiEvalGet {
__typename
}
`;
export const DUMMY_UPDATE = gql`
mutation DummyAiEvalUpdate {
__typename
}
`;
export const DUMMY_DELETE = gql`
mutation DummyAiEvalDelete {
__typename
}
`;
export const DUMMY_CREATE = gql`
mutation DummyAiEvalCreate {
__typename
}
`;

const goldenQAHelperContent = (
<div className={styles.GoldenQAHelper}>
<p className={styles.GoldenQAHelperDescription}>
Select the Golden QA dataset from the existing list or upload a new set of Golden QA in CSV format to run the
evaluation on.
Select The Golden QA Dataset From Existing List Or Upload A New Golden Set Of QA In Csv Format To Run The
Evaluation On.
</p>
<div className={styles.CSVFormatBox}>
<div className={styles.CSVFormatLabel}>Expected CSV Format:</div>
<div className={styles.CSVFormatExample}>Question,Answer</div>
<div className={styles.CSVFormatExample}>{'What is the capital of France?,Paris'}</div>
<div className={styles.CSVFormatExample}>Question, Answer</div>
<div className={styles.CSVFormatExample}>{'{"What Is X"},{"Answer"}'}</div>
<a href="#" className={styles.TemplateLink} target="_blank" rel="noopener noreferrer">
Click here for the template CSV
Click Here For The Template Csv
</a>
</div>
</div>
);

const GoldenQaField = (props: any) => {
const { onUploadGoldenQaClick, form, ...dropdownProps } = props;
const { onUploadGoldenQaClick, form, helperText, ...dropdownProps } = props;
return (
<div className={styles.GoldenQaRow}>
<Dropdown {...dropdownProps} form={form} />
<Button
variant="outlined"
color="primary"
type="button"
className={styles.UploadGoldenQaButton}
onClick={onUploadGoldenQaClick}
>
Upload Golden QA
</Button>
<div>
<div className={styles.GoldenQaRow}>
<Dropdown {...dropdownProps} form={form} />
<Button
variant="outlined"
color="primary"
type="button"
className={styles.UploadGoldenQaButton}
onClick={onUploadGoldenQaClick}
>
Upload Golden QA
</Button>
</div>
{helperText && <div className={styles.GoldenQaHelperText}>{helperText}</div>}
</div>
);
};

const SectionDivider = (_props: any) => null;

export default function AIEvaluationCreate() {
const [goldenQADatasets, setGoldenQADatasets] = useState<string[]>([]);
const navigate = useNavigate();
const [goldenQADatasets, setGoldenQADatasets] = useState<Array<{ datasetId: number; name: string }>>([]);
const [showUploadGoldenQaDialog, setShowUploadGoldenQaDialog] = useState(false);
const [selectedGoldenQaFileName, setSelectedGoldenQaFileName] = useState<string | null>(null);
const [selectedGoldenQaFile, setSelectedGoldenQaFile] = useState<File | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);

const [createEvaluation, { loading: evaluationLoading }] = useMutation(CREATE_EVALUATION, {
onCompleted: () => {
setNotification('Evaluation started successfully!');
navigate('/chat');
},
onError: (error) => {
setNotification(error.message, 'warning');
},
});

const goldenQaOptions =
goldenQADatasets.length === 0
? [{ id: '0', label: 'No Golden QA available, upload one first' }]
: goldenQADatasets.map((name) => ({ id: name, label: name }));
const assistantOptions = [{ id: '', label: 'Pick your assistant & version to evaluate' }];
: goldenQADatasets.map(({ datasetId, name }) => ({ id: datasetId, label: name }));

const { data: versionsData } = useQuery(GET_ASSISTANT_CONFIG_VERSIONS, {
variables: { filter: {} },
});

const assistantOptions =
versionsData?.assistantConfigVersions?.length > 0
? versionsData.assistantConfigVersions.map((v: any) => ({
id: v.id,
label: `${v.assistantName} (Version ${v.versionNumber})`,
}))
: [{ id: '', label: 'No assistants available' }];

const validationSchema = Yup.object().shape({
evaluationName: Yup.string()
Expand All @@ -87,9 +97,9 @@ export default function AIEvaluationCreate() {
assistantId: Yup.string().required('Please select an AI Assistant'),
});

const [states, setStates] = useState<{ evaluationName: string; goldenQaId: string; assistantId: string }>({
const [states, setStates] = useState<{ evaluationName: string; goldenQaId: number; assistantId: string }>({
evaluationName: '',
goldenQaId: '0',
goldenQaId: 0,
assistantId: '',
});

Expand All @@ -110,10 +120,9 @@ export default function AIEvaluationCreate() {
setShowUploadGoldenQaDialog(true);
};

const handleUploadGoldenQaProceed = (values: { name: string }) => {
// Placeholder for when backend mutation response needs to be used further. New upload at top.
setGoldenQADatasets((prev) => [values.name, ...prev]);
setStates((prev) => ({ ...prev, goldenQaId: values.name }));
const handleUploadGoldenQaProceed = (values: { datasetId: number; name: string }) => {
setGoldenQADatasets((prev) => [{ datasetId: values.datasetId, name: values.name }, ...prev]);
setStates((prev) => ({ ...prev, goldenQaId: values.datasetId }));
setShowUploadGoldenQaDialog(false);
};

Expand All @@ -127,24 +136,46 @@ export default function AIEvaluationCreate() {
helperText: goldenQAHelperContent,
onUploadGoldenQaClick: handleUploadGoldenQaButtonClick,
},
{
component: SectionDivider,
name: '__evaluationDetailsDivider',
label: 'Evaluation Details',
placedolder: '',
},
{
component: Input,
name: 'evaluationName',
type: 'text',
label: 'Evaluation Name*',
placeholder: 'Give a unique name for the evaluation experiment.',
placeholder: 'Give a unique name for the evaluation experiment',
},
{
component: Dropdown,
name: 'assistantId',
label: 'AI Assistant*',
options: assistantOptions,
placeholder: '',
helperText: "This list includes all assistants and versions you've created.",
},
];

const dialogMessage = 'This action cannot be undone.';

const handleSetPayload = (payload: any) => {
const selectedVersion = versionsData?.assistantConfigVersions?.find((v: any) => v.id === payload.assistantId);
createEvaluation({
variables: {
input: {
datasetId: payload.goldenQaId,
experimentName: payload.evaluationName,
configId: selectedVersion?.kaapiUuid ?? payload.assistantId,
configVersion: payload.assistantId,
},
},
});
return payload;
};

return (
<div>
<FormLayout
Expand All @@ -156,10 +187,10 @@ export default function AIEvaluationCreate() {
formFields={formFields}
redirectionLink="ai-evaluations"
listItem="aiEvaluation"
getItemQuery={DUMMY_GET_ITEM}
createItemQuery={DUMMY_CREATE}
updateItemQuery={DUMMY_UPDATE}
deleteItemQuery={DUMMY_DELETE}
getItemQuery={GET_ASSISTANT_CONFIG_VERSIONS}
createItemQuery={CREATE_EVALUATION}
updateItemQuery={CREATE_EVALUATION}
deleteItemQuery={CREATE_EVALUATION}
defaultAttribute={null}
icon={null}
refetchQueries={[]}
Expand All @@ -177,7 +208,9 @@ export default function AIEvaluationCreate() {
noHeading={false}
partialPage={false}
confirmationState={{ show: false, title: '', message: '' }}
customStyles={styles.FormLayout}
setPayload={handleSetPayload}
customHandler={() => {}}
buttonState={{ status: evaluationLoading, text: 'Running...', styles: '', show: true }}
/>
<input
ref={fileInputRef}
Expand All @@ -197,4 +230,4 @@ export default function AIEvaluationCreate() {
)}
</div>
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ describe('UploadGoldenQaDialog', () => {
expect(notificationSpy).toHaveBeenCalledWith('Golden QA uploaded successfully', 'success');
});
expect(onProceed).toHaveBeenCalledWith({
datasetId: 123,
name: 'golden_qa',
duplicationFactor: 1,
});
Expand All @@ -215,6 +216,7 @@ describe('UploadGoldenQaDialog', () => {

await waitFor(() => {
expect(onProceed).toHaveBeenCalledWith({
datasetId: 456,
name: 'my_custom_name',
duplicationFactor: 3,
});
Expand Down
Loading
Loading