diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 8b7a0e8..cf5257c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,9 +5,7 @@ const path = require("path"); const config = { overrides: [ { - extends: [ - "plugin:@typescript-eslint/recommended-requiring-type-checking", - ], + extends: ["plugin:@typescript-eslint/recommended-requiring-type-checking"], files: ["*.ts", "*.tsx"], parserOptions: { project: path.join(__dirname, "tsconfig.json"), @@ -29,6 +27,7 @@ const config = { }, ], "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], + "@typescript-eslint/no-unsafe-member-access": "off", }, }; diff --git a/package.json b/package.json index 641c87c..cc7c9b6 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@emotion/react": "^11.11.1", "@emotion/server": "^11.11.0", "@mantine/core": "^6.0.14", + "@mantine/dates": "^6.0.14", "@mantine/form": "^6.0.14", "@mantine/hooks": "^6.0.14", "@mantine/next": "^6.0.14", @@ -26,7 +27,9 @@ "@trpc/next": "^10.26.0", "@trpc/react-query": "^10.26.0", "@trpc/server": "^10.26.0", + "dayjs": "^1.11.8", "dotenv": "^16.3.1", + "mantine-react-table": "1.0.0-beta.13", "next": "^13.4.2", "next-auth": "^4.22.1", "react": "18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c3c30f..d0a816a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@mantine/core': specifier: ^6.0.14 version: 6.0.14(@emotion/react@11.11.1)(@mantine/hooks@6.0.14)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) + '@mantine/dates': + specifier: ^6.0.14 + version: 6.0.14(@mantine/core@6.0.14)(@mantine/hooks@6.0.14)(dayjs@1.11.8)(react@18.2.0) '@mantine/form': specifier: ^6.0.14 version: 6.0.14(react@18.2.0) @@ -50,9 +53,15 @@ dependencies: '@trpc/server': specifier: ^10.26.0 version: 10.26.0 + dayjs: + specifier: ^1.11.8 + version: 1.11.8 dotenv: specifier: ^16.3.1 version: 16.3.1 + mantine-react-table: + specifier: 1.0.0-beta.13 + version: 1.0.0-beta.13(@emotion/react@11.11.1)(@mantine/core@6.0.14)(@mantine/dates@6.0.14)(@mantine/hooks@6.0.14)(@tabler/icons-react@2.22.0)(react-dom@18.2.0)(react@18.2.0) next: specifier: ^13.4.2 version: 13.4.2(react-dom@18.2.0)(react@18.2.0) @@ -591,6 +600,21 @@ packages: - '@types/react' dev: false + /@mantine/dates@6.0.14(@mantine/core@6.0.14)(@mantine/hooks@6.0.14)(dayjs@1.11.8)(react@18.2.0): + resolution: {integrity: sha512-iQxZcqTpH/sHv1ZdCru3hFLlXQFA+qT/19a1EmtSJrspITZQJ8xYf8xSkkLyY6Wc6KviX2Lp0fSDE72mQvNbyg==} + peerDependencies: + '@mantine/core': 6.0.14 + '@mantine/hooks': 6.0.14 + dayjs: '>=1.0.0' + react: '>=16.8.0' + dependencies: + '@mantine/core': 6.0.14(@emotion/react@11.11.1)(@mantine/hooks@6.0.14)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) + '@mantine/hooks': 6.0.14(react@18.2.0) + '@mantine/utils': 6.0.14(react@18.2.0) + dayjs: 1.11.8 + react: 18.2.0 + dev: false + /@mantine/form@6.0.14(react@18.2.0): resolution: {integrity: sha512-2QlDN3PBMxHUxtoBy0ycc3InpATGje5sJXmw/Co9qiVtKUHe5pxcVl341CnA+MCI91uC2Ycucf20n/8GTLezrw==} peerDependencies: @@ -981,6 +1005,13 @@ packages: resolution: {integrity: sha512-lOsGHqRPIKNARMWHHFkUUJH78C8ptQmUcDnumFBUI4YLRKFouKa7uAZL3ZfuH0HjDpOhsnWqUYZ7FhMCLcGpAQ==} dev: false + /@tanstack/match-sorter-utils@8.8.4: + resolution: {integrity: sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==} + engines: {node: '>=12'} + dependencies: + remove-accents: 0.4.2 + dev: false + /@tanstack/query-core@4.29.7: resolution: {integrity: sha512-GXG4b5hV2Loir+h2G+RXhJdoZhJLnrBWsuLB2r0qBRyhWuXq9w/dWxzvpP89H0UARlH6Mr9DiVj4SMtpkF/aUA==} dev: false @@ -1003,6 +1034,36 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false + /@tanstack/react-table@8.9.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Irvw4wqVF9hhuYzmNrlae4IKdlmgSyoRWnApSLebvYzqHoi5tEsYzBj6YPd0hX78aB/L+4w/jgK2eBQVpGfThQ==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@tanstack/table-core': 8.9.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tanstack/react-virtual@3.0.0-beta.54(react@18.2.0): + resolution: {integrity: sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/virtual-core': 3.0.0-beta.54 + react: 18.2.0 + dev: false + + /@tanstack/table-core@8.9.2: + resolution: {integrity: sha512-ajc0OF+karBAdaSz7OK09rCoAHB1XI1+wEhu+tDNMPc+XcO+dTlXXN/Vc0a8vym4kElvEjXEDd9c8Zfgt4bekA==} + engines: {node: '>=12'} + dev: false + + /@tanstack/virtual-core@3.0.0-beta.54: + resolution: {integrity: sha512-jtkwqdP2rY2iCCDVAFuaNBH3fiEi29aTn2RhtIoky8DTTiCdc48plpHHreLwmv1PICJ4AJUUESaq3xa8fZH8+g==} + dev: false + /@trpc/client@10.26.0(@trpc/server@10.26.0): resolution: {integrity: sha512-ojHxQFIE97rBEGPK8p1ijbzo0T1IdEBoJ9fFSgWWL9FMuEEA/DNQ9s0uuiOrDKhCCdTFT1unfRharoJhB2/O2w==} peerDependencies: @@ -1542,6 +1603,10 @@ packages: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true + /dayjs@1.11.8: + resolution: {integrity: sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==} + dev: false + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2379,6 +2444,11 @@ packages: dependencies: function-bind: 1.1.1 + /highlight-words@1.2.2: + resolution: {integrity: sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ==} + engines: {node: '>= 16', npm: '>= 8'} + dev: false + /hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} dependencies: @@ -2751,6 +2821,31 @@ packages: dependencies: yallist: 4.0.0 + /mantine-react-table@1.0.0-beta.13(@emotion/react@11.11.1)(@mantine/core@6.0.14)(@mantine/dates@6.0.14)(@mantine/hooks@6.0.14)(@tabler/icons-react@2.22.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GOC536sjdsWgct9ErNjyWmn54PHxAt6PcvnGnUCeECF5MoyY2mNO3ukgD377dGL0b+e+kjCXCxY4W7MHV1/WqQ==} + engines: {node: '>=14'} + peerDependencies: + '@emotion/react': '>=11' + '@mantine/core': '>=6' + '@mantine/dates': '>=6' + '@mantine/hooks': '>=6' + '@tabler/icons-react': '>=2' + react: '>=17.0' + react-dom: '>=17.0' + dependencies: + '@emotion/react': 11.11.1(@types/react@18.2.6)(react@18.2.0) + '@mantine/core': 6.0.14(@emotion/react@11.11.1)(@mantine/hooks@6.0.14)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) + '@mantine/dates': 6.0.14(@mantine/core@6.0.14)(@mantine/hooks@6.0.14)(dayjs@1.11.8)(react@18.2.0) + '@mantine/hooks': 6.0.14(react@18.2.0) + '@tabler/icons-react': 2.22.0(react@18.2.0) + '@tanstack/match-sorter-utils': 8.8.4 + '@tanstack/react-table': 8.9.2(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-virtual': 3.0.0-beta.54(react@18.2.0) + highlight-words: 1.2.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -3281,6 +3376,10 @@ packages: functions-have-names: 1.2.3 dev: true + /remove-accents@0.4.2: + resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==} + dev: false + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} diff --git a/src/components/OutputsTable/index.tsx b/src/components/OutputsTable/index.tsx new file mode 100644 index 0000000..9810e1d --- /dev/null +++ b/src/components/OutputsTable/index.tsx @@ -0,0 +1,64 @@ +import { MRT_ColumnDef, MantineReactTable } from "mantine-react-table"; +import { useMemo } from "react"; +import { RouterOutputs, api } from "~/utils/api"; + +type CellData = { + variant: NonNullable[0]; + scenario: NonNullable[0]; +}; + +type TableRow = { + scenario: NonNullable[0]; +} & Record; + +export default function OutputsTable({ experimentId }: { experimentId: string }) { + const experiment = api.experiments.get.useQuery({ id: experimentId }); + + const variants = api.promptVariants.list.useQuery({ experimentId: experimentId }); + + const scenarios = api.scenarios.list.useQuery({ experimentId: experimentId }); + + const columns = useMemo[]>( + () => [ + { + id: "scenario", + header: "Scenario", + Cell: ({ row }) => { + return
{JSON.stringify(row.original.scenario.variableValues)}
; + }, + }, + ...(variants.data?.map( + (variant): MRT_ColumnDef => ({ + id: variant.id, + header: variant.label, + Cell: ({ row }) => { + const cellData = row.original[variant.id]; + return ( +
+ {row.original.scenario.id} | {variant.id} +
+ ); + }, + }) + ) ?? []), + ], + [variants.data] + ); + + const tableData = useMemo( + () => + scenarios.data?.map((scenario) => { + return { + scenario, + // ...variants.data?.reduce( + // (acc, variant) => ({ ...acc, [variant.id]: { variant, scenario } }), + // {} as Record + // ), + } as TableRow; + }) ?? [], + [variants.data, scenarios.data] + ); + + return ; + // return
OutputsTable
; +} diff --git a/src/components/nav/AppNav.tsx b/src/components/nav/AppNav.tsx index f3b1f48..bdc4697 100644 --- a/src/components/nav/AppNav.tsx +++ b/src/components/nav/AppNav.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { createStyles, Navbar, Group, Code, getStylesRef, rem } from "@mantine/core"; +import { createStyles, Navbar, Group, Code, getStylesRef, rem, Box } from "@mantine/core"; import { IconBellRinging, IconFingerprint, @@ -11,6 +11,7 @@ import { IconSwitchHorizontal, IconLogout, } from "@tabler/icons-react"; +import Head from "next/head"; // import { MantineLogo } from '@mantine/ds'; const useStyles = createStyles((theme) => ({ @@ -78,7 +79,7 @@ const data = [ { link: "", label: "Other Settings", icon: IconSettings }, ]; -export default function AppNav({ children }: { children: React.ReactNode }) { +export default function AppNav(props: { children: React.ReactNode; title?: string }) { const { classes, cx } = useStyles(); const [active, setActive] = useState("Billing"); @@ -99,7 +100,10 @@ export default function AppNav({ children }: { children: React.ReactNode }) { return ( - + + {props.title && `${props.title} | `}Prompt Bench + + {/* */} @@ -120,7 +124,7 @@ export default function AppNav({ children }: { children: React.ReactNode }) { - {children} + {props.children} ); } diff --git a/src/pages/experiments/[id].tsx b/src/pages/experiments/[id].tsx index d7917bd..23ff10e 100644 --- a/src/pages/experiments/[id].tsx +++ b/src/pages/experiments/[id].tsx @@ -1,9 +1,27 @@ +import { Center } from "@mantine/core"; +import { useRouter } from "next/router"; +import OutputsTable from "~/components/OutputsTable"; import AppNav from "~/components/nav/AppNav"; +import { api } from "~/utils/api"; export default function Experiment() { + const router = useRouter(); + + const experiment = api.experiments.get.useQuery({ id: router.query.id as string }); + + if (!experiment.data) { + return ( + +
+
Experiment not found 😕
+
+
+ ); + } + return ( - -
test content
+ + ); } diff --git a/src/server/api/root.router.ts b/src/server/api/root.router.ts index beeda57..32e303f 100644 --- a/src/server/api/root.router.ts +++ b/src/server/api/root.router.ts @@ -1,5 +1,7 @@ import { promptVariantsRouter } from "~/server/api/routers/promptVariants.router"; import { createTRPCRouter } from "~/server/api/trpc"; +import { experimentsRouter } from "./routers/experiments.router"; +import { scenariosRouter } from "./routers/scenarios.router"; /** * This is the primary router for your server. @@ -8,6 +10,8 @@ import { createTRPCRouter } from "~/server/api/trpc"; */ export const appRouter = createTRPCRouter({ promptVariants: promptVariantsRouter, + experiments: experimentsRouter, + scenarios: scenariosRouter, }); // export type definition of API diff --git a/src/server/api/routers/experiments.router.ts b/src/server/api/routers/experiments.router.ts new file mode 100644 index 0000000..c2b0ccf --- /dev/null +++ b/src/server/api/routers/experiments.router.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; +import { createTRPCRouter, publicProcedure, protectedProcedure } from "~/server/api/trpc"; +import { prisma } from "~/server/db"; + +export const experimentsRouter = createTRPCRouter({ + get: publicProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { + return await prisma.experiment.findFirst({ + where: { + id: input.id, + }, + }); + }), +}); diff --git a/src/server/api/routers/promptVariants.router.ts b/src/server/api/routers/promptVariants.router.ts index 9178dec..ff3a254 100644 --- a/src/server/api/routers/promptVariants.router.ts +++ b/src/server/api/routers/promptVariants.router.ts @@ -3,15 +3,11 @@ import { createTRPCRouter, publicProcedure, protectedProcedure } from "~/server/ import { prisma } from "~/server/db"; export const promptVariantsRouter = createTRPCRouter({ - getAll: publicProcedure.input(z.object({ experimentId: z.string() })).query(async ({ input }) => { + list: publicProcedure.input(z.object({ experimentId: z.string() })).query(async ({ input }) => { return await prisma.promptVariant.findMany({ where: { experimentId: input.experimentId, }, }); }), - - getSecretMessage: protectedProcedure.query(() => { - return "you can now see this secret message!"; - }), }); diff --git a/src/server/api/routers/scenarios.router.ts b/src/server/api/routers/scenarios.router.ts new file mode 100644 index 0000000..0409c80 --- /dev/null +++ b/src/server/api/routers/scenarios.router.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; +import { createTRPCRouter, publicProcedure, protectedProcedure } from "~/server/api/trpc"; +import { prisma } from "~/server/db"; + +export const scenariosRouter = createTRPCRouter({ + list: publicProcedure.input(z.object({ experimentId: z.string() })).query(async ({ input }) => { + return await prisma.testScenario.findMany({ + where: { + experimentId: input.experimentId, + }, + }); + }), +});