Skip to content

Commit

Permalink
feat: add overview tab for model configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatanvesh committed Nov 11, 2023
1 parent 739a49a commit 8d2b934
Show file tree
Hide file tree
Showing 25 changed files with 998 additions and 39 deletions.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"react-intl": "^6.5.2",
"react-tailwindcss-datepicker": "^1.6.6",
"swr": "^2.2.4",
"unique-names-generator": "^4.7.1",
"zustand": "^4.4.6"
},
"devDependencies": {
Expand Down
15 changes: 15 additions & 0 deletions frontend/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,21 @@
"promptHeader": "Prompt Testing",
"savedPromptConfig": "Saved Templates"
},
"promptConfig": {
"apiCalls": "API Calls",
"clone": "Clone",
"configCloned": "Successfully cloned",
"general": "General",
"id": "ID",
"modelConfiguration": "Model Configuration",
"modelsCost": "Models Cost",
"name": "Name",
"noOfVariables": "Number of variables",
"overview": "Overview",
"partOfApplication": "Part of application",
"status": "Status",
"test": "Test"
},
"signin": {
"authHeader": "Welcome to",
"authSubtitle": "Integrate in minutes, scale to millions",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use client';

import { useTranslations } from 'next-intl';
import { useState } from 'react';
import { Speedometer2 } from 'react-bootstrap-icons';
import useSWR from 'swr';

import { handleRetrievePromptConfigs } from '@/api';
import { PromptAnalyticsPage } from '@/components/projects/[projectId]/applications/[applicationId]/prompts/[promptId]/prompt-analytics-page';
import { PromptGeneralInfo } from '@/components/projects/[projectId]/applications/[applicationId]/prompts/[promptId]/prompt-general-info';
import { TabData, TabNavigation } from '@/components/tab-navigation';
import { ApiError } from '@/errors';
import { useAuthenticatedUser } from '@/hooks/use-authenticated-user';
import { useProjectBootstrap } from '@/hooks/use-project-bootstrap';
import { usePromptConfig, useSetPromptConfigs } from '@/stores/project-store';
import { useShowError } from '@/stores/toast-store';

enum TAB_NAMES {
OVERVIEW,
MODEL,
PROMPT,
SETTINGS,
}

export default function PromptConfiguration({
params: { projectId, applicationId, promptConfigId },
}: {
params: {
projectId: string;
applicationId: string;
promptConfigId: string;
};
}) {
useAuthenticatedUser();
useProjectBootstrap(false);

const t = useTranslations('promptConfig');
const showError = useShowError();

const promptConfig = usePromptConfig(applicationId, promptConfigId);
const setPromptConfigs = useSetPromptConfigs();

const { isLoading } = useSWR(
promptConfig ? null : { projectId, applicationId },
handleRetrievePromptConfigs,
{
onError({ message }: ApiError) {
showError(message);
},
onSuccess(promptConfigs) {
setPromptConfigs(applicationId, promptConfigs);
},
},
);

const tabs: TabData<TAB_NAMES>[] = [
{
id: TAB_NAMES.OVERVIEW,
text: t('overview'),
icon: <Speedometer2 className="w-3.5 h-3.5" />,
},
];
const [selectedTab, setSelectedTab] = useState(TAB_NAMES.OVERVIEW);

if (!promptConfig && isLoading) {
return (
<div
data-testid="prompt-config-page-loading"
className="h-full w-full flex items-center justify-center"
>
<span className="loading loading-spinner loading-md" />
</div>
);
} else if (promptConfig) {
return (
<div data-testid="prompt-page" className="my-8 mx-32">
<h1
data-testid="prompt-page-title"
className="text-2xl font-semibold text-base-content"
>
{t('modelConfiguration')} / {promptConfig.name}
</h1>
<div className="mt-3.5 w-full mb-8">
<TabNavigation<TAB_NAMES>
tabs={tabs}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
trailingLine={true}
/>
</div>
{selectedTab === TAB_NAMES.OVERVIEW && (
<>
<PromptAnalyticsPage
applicationId={applicationId}
projectId={projectId}
promptConfigId={promptConfigId}
/>
<div className="h-8" />
<PromptGeneralInfo
applicationId={applicationId}
projectId={projectId}
promptConfigId={promptConfigId}
/>
</>
)}
</div>
);
} else {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { Navigation } from '@/constants';
import { ApiError } from '@/errors';
import {
useApplications,
usePromptConfig,
usePromptConfigs,
useSetProjectApplications,
useSetPromptConfig,
useSetPromptConfigs,
} from '@/stores/project-store';
import { useShowError } from '@/stores/toast-store';
import { populateApplicationId, populateProjectId } from '@/utils/navigation';
Expand All @@ -22,8 +22,8 @@ export function ApplicationsList({ projectId }: { projectId: string }) {
const applications = useApplications(projectId);
const setProjectApplications = useSetProjectApplications();

const promptConfigs = usePromptConfig();
const setPromptConfig = useSetPromptConfig();
const promptConfigs = usePromptConfigs();
const setPromptConfig = useSetPromptConfigs();

const dialogRef = useRef<HTMLDialogElement>(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { MIN_NAME_LENGTH } from '@/constants';
import { ApiError } from '@/errors';
import {
useApplication,
usePromptConfig,
useSetPromptConfig,
usePromptConfigs,
useSetPromptConfigs,
useUpdateApplication,
} from '@/stores/project-store';
import { useShowError } from '@/stores/toast-store';
Expand Down Expand Up @@ -41,8 +41,8 @@ export function ApplicationGeneralSettings({
const [defaultPromptConfig, setDefaultPromptConfig] = useState<
string | undefined
>();
const setPromptConfig = useSetPromptConfig();
const promptConfigs = usePromptConfig();
const setPromptConfig = useSetPromptConfigs();
const promptConfigs = usePromptConfigs();

const isChanged =
name !== application?.name ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { Front, PencilFill, Plus, Search } from 'react-bootstrap-icons';
import useSWR from 'swr';

import { handleRetrievePromptConfigs } from '@/api';
import { Navigation } from '@/constants';
import { ApiError } from '@/errors';
import { usePromptConfig, useSetPromptConfig } from '@/stores/project-store';
import { usePromptConfigs, useSetPromptConfigs } from '@/stores/project-store';
import { useShowError, useShowSuccess } from '@/stores/toast-store';
import { copyToClipboard } from '@/utils/helpers';
import { populateLink } from '@/utils/navigation';

export function ApplicationPromptConfigs({
projectId,
Expand All @@ -16,8 +19,10 @@ export function ApplicationPromptConfigs({
applicationId: string;
}) {
const t = useTranslations('application');
const setPromptConfig = useSetPromptConfig();
const promptConfigs = usePromptConfig();
const router = useRouter();

const setPromptConfig = useSetPromptConfigs();
const promptConfigs = usePromptConfigs();

const showError = useShowError();
const showSuccess = useShowSuccess();
Expand All @@ -38,6 +43,17 @@ export function ApplicationPromptConfigs({
},
);

function editPrompt(promptId: string) {
router.push(
populateLink(
Navigation.Prompts,
projectId,
applicationId,
promptId,
),
);
}

function renderPromptConfigs() {
if (isLoading) {
return (
Expand Down Expand Up @@ -83,7 +99,12 @@ export function ApplicationPromptConfigs({
</button>
</td>
<td>
<button>
<button
data-testid="application-edit-prompt-button"
onClick={() => {
editPrompt(id);
}}
>
<PencilFill className="w-3.5 h-3.5 text-secondary" />
</button>
</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useTranslations } from 'next-intl';
import { useState } from 'react';
import { Activity, Cash } from 'react-bootstrap-icons';
import { DateValueType } from 'react-tailwindcss-datepicker';
import useSWR from 'swr';

import { handlePromptConfigAnalytics } from '@/api';
import { DataCard } from '@/components/dashboard/data-card';
import { DatePicker } from '@/components/dashboard/date-picker';
import { ApiError } from '@/errors';
import { useShowError } from '@/stores/toast-store';
import { useDateFormat } from '@/stores/user-config-store';

export function PromptAnalyticsPage({
projectId,
applicationId,
promptConfigId,
}: {
projectId: string;
applicationId: string;
promptConfigId: string;
}) {
const t = useTranslations('promptConfig');
const dateFormat = useDateFormat();
const showError = useShowError();

const oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);

const [dateRange, setDateRange] = useState<DateValueType>({
startDate: oneWeekAgo,
endDate: new Date(),
});

const { data: analytics, isLoading } = useSWR(
{
projectId,
applicationId,
promptConfigId,
fromDate: dateRange?.startDate,
toDate: dateRange?.endDate,
},
handlePromptConfigAnalytics,
{
onError({ message }: ApiError) {
showError(message);
},
},
);

return (
<div data-testid="prompt-analytics-container">
<div className="flex justify-between items-center">
<h2 className="font-semibold text-white text-xl">
{t('status')}
</h2>
<DatePicker
displayFormat={dateFormat}
showShortcuts={true}
useRange={true}
value={dateRange}
onValueChange={setDateRange}
/>
</div>
<div className="flex items-center justify-between custom-card">
<DataCard
imageSrc={<Activity className="text-secondary w-6 h-6" />}
metric={t('apiCalls')}
totalValue={analytics?.totalRequests ?? ''}
percentage={'100'}
currentValue={'324'}
loading={isLoading}
/>
<div className="w-px h-12 bg-gray-200 mx-4" />
<DataCard
imageSrc={<Cash className="text-secondary w-6 h-6" />}
metric={t('modelsCost')}
totalValue={`${analytics?.tokensCost ?? ''}$`}
percentage={'103'}
currentValue={'3.3'}
loading={isLoading}
/>
</div>
</div>
);
}
Loading

0 comments on commit 8d2b934

Please sign in to comment.