can add scenarios and it mostly works
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@fontsource/poppins": "^5.0.3",
|
||||||
|
"@fontsource/roboto": "^5.0.3",
|
||||||
"@monaco-editor/react": "^4.5.1",
|
"@monaco-editor/react": "^4.5.1",
|
||||||
"@next-auth/prisma-adapter": "^1.0.5",
|
"@next-auth/prisma-adapter": "^1.0.5",
|
||||||
"@prisma/client": "^4.14.0",
|
"@prisma/client": "^4.14.0",
|
||||||
@@ -34,6 +36,8 @@
|
|||||||
"next-auth": "^4.22.1",
|
"next-auth": "^4.22.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-icons": "^4.10.1",
|
||||||
|
"react-textarea-autosize": "^8.5.0",
|
||||||
"superjson": "1.12.2",
|
"superjson": "1.12.2",
|
||||||
"tsx": "^3.12.7",
|
"tsx": "^3.12.7",
|
||||||
"zod": "^3.21.4"
|
"zod": "^3.21.4"
|
||||||
|
|||||||
77
pnpm-lock.yaml
generated
77
pnpm-lock.yaml
generated
@@ -20,6 +20,12 @@ dependencies:
|
|||||||
'@emotion/styled':
|
'@emotion/styled':
|
||||||
specifier: ^11.11.0
|
specifier: ^11.11.0
|
||||||
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.6)(react@18.2.0)
|
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.6)(react@18.2.0)
|
||||||
|
'@fontsource/poppins':
|
||||||
|
specifier: ^5.0.3
|
||||||
|
version: 5.0.3
|
||||||
|
'@fontsource/roboto':
|
||||||
|
specifier: ^5.0.3
|
||||||
|
version: 5.0.3
|
||||||
'@monaco-editor/react':
|
'@monaco-editor/react':
|
||||||
specifier: ^4.5.1
|
specifier: ^4.5.1
|
||||||
version: 4.5.1(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0)
|
version: 4.5.1(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@@ -74,6 +80,12 @@ dependencies:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0(react@18.2.0)
|
version: 18.2.0(react@18.2.0)
|
||||||
|
react-icons:
|
||||||
|
specifier: ^4.10.1
|
||||||
|
version: 4.10.1(react@18.2.0)
|
||||||
|
react-textarea-autosize:
|
||||||
|
specifier: ^8.5.0
|
||||||
|
version: 8.5.0(@types/react@18.2.6)(react@18.2.0)
|
||||||
superjson:
|
superjson:
|
||||||
specifier: 1.12.2
|
specifier: 1.12.2
|
||||||
version: 1.12.2
|
version: 1.12.2
|
||||||
@@ -1670,6 +1682,14 @@ packages:
|
|||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@fontsource/poppins@5.0.3:
|
||||||
|
resolution: {integrity: sha512-5lL2vmvmh4zbknTh1nVH9pTWyhYqAlYMHIAnkNhqo5qwMZGlr+coM1dtMwiQHRBgmHAl3ZvJ35Bj0s8cpmXZbg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@fontsource/roboto@5.0.3:
|
||||||
|
resolution: {integrity: sha512-jbZDFwEFARDlo8TqG7th/xjhuq87GYfFpFb+uxuy+0Ng1bhRVgBRWlLj8+WIKhCTOr+h4QXbjpybLWFLUirOwQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@humanwhocodes/config-array@0.11.10:
|
/@humanwhocodes/config-array@0.11.10:
|
||||||
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
@@ -4135,6 +4155,14 @@ packages:
|
|||||||
use-sidecar: 1.1.2(@types/react@18.2.6)(react@18.2.0)
|
use-sidecar: 1.1.2(@types/react@18.2.6)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-icons@4.10.1(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '*'
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-is@16.13.1:
|
/react-is@16.13.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
|
|
||||||
@@ -4198,6 +4226,20 @@ packages:
|
|||||||
tslib: 2.5.3
|
tslib: 2.5.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-textarea-autosize@8.5.0(@types/react@18.2.6)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-cp488su3U9RygmHmGpJp0KEt0i/+57KCK33XVPH+50swVRBhIZYh0fGduz2YLKXwl9vSKBZ9HUXcg9PQXUXqIw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.22.5
|
||||||
|
react: 18.2.0
|
||||||
|
use-composed-ref: 1.3.0(react@18.2.0)
|
||||||
|
use-latest: 1.2.1(@types/react@18.2.6)(react@18.2.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/react'
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react@18.2.0:
|
/react@18.2.0:
|
||||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -4658,6 +4700,41 @@ packages:
|
|||||||
tslib: 2.5.3
|
tslib: 2.5.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/use-composed-ref@1.3.0(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/use-isomorphic-layout-effect@1.1.2(@types/react@18.2.6)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@types/react': 18.2.6
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/use-latest@1.2.1(@types/react@18.2.6)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@types/react': 18.2.6
|
||||||
|
react: 18.2.0
|
||||||
|
use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.6)(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/use-sidecar@1.1.2(@types/react@18.2.6)(react@18.2.0):
|
/use-sidecar@1.1.2(@types/react@18.2.6)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ export default function EditableVariantLabel(props: { variant: PromptVariant })
|
|||||||
ref={labelRef}
|
ref={labelRef}
|
||||||
contentEditable
|
contentEditable
|
||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
|
borderWidth={1}
|
||||||
|
borderColor="transparent"
|
||||||
|
_hover={{ borderColor: "gray.300" }}
|
||||||
|
_focus={{ borderColor: "blue.500", outline: "none" }}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
|
py={2}
|
||||||
>
|
>
|
||||||
{props.variant.label}
|
{props.variant.label}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|||||||
34
src/components/OutputsTable/NewScenarioButton.tsx
Normal file
34
src/components/OutputsTable/NewScenarioButton.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Button } from "@chakra-ui/react";
|
||||||
|
import { BsPlus } from "react-icons/bs";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
|
|
||||||
|
export default function NewScenarioButton() {
|
||||||
|
const experiment = useExperiment();
|
||||||
|
const mutation = api.scenarios.create.useMutation();
|
||||||
|
const utils = api.useContext();
|
||||||
|
|
||||||
|
const [onClick] = useHandledAsyncCallback(async () => {
|
||||||
|
await mutation.mutateAsync({
|
||||||
|
experimentId: experiment.data!.id,
|
||||||
|
});
|
||||||
|
await utils.scenarios.list.invalidate();
|
||||||
|
}, [mutation]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
w="100%"
|
||||||
|
borderRadius={0}
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="flex-start"
|
||||||
|
fontWeight="normal"
|
||||||
|
bgColor="transparent"
|
||||||
|
_hover={{ bgColor: "gray.100" }}
|
||||||
|
px={2}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<BsPlus size={24} />
|
||||||
|
New Scenario
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { PromptVariant, Scenario } from "./types";
|
import { PromptVariant, Scenario } from "./types";
|
||||||
import { Center } from "@chakra-ui/react";
|
import { Center, Text } from "@chakra-ui/react";
|
||||||
|
import { useExperiment } from "~/utils/hooks";
|
||||||
|
|
||||||
export default function OutputCell({
|
export default function OutputCell({
|
||||||
scenario,
|
scenario,
|
||||||
@@ -9,12 +10,32 @@ export default function OutputCell({
|
|||||||
scenario: Scenario;
|
scenario: Scenario;
|
||||||
variant: PromptVariant;
|
variant: PromptVariant;
|
||||||
}) {
|
}) {
|
||||||
const output = api.outputs.get.useQuery({
|
const experiment = useExperiment();
|
||||||
|
|
||||||
|
const experimentVariables = experiment.data?.TemplateVariable.map((v) => v.label) ?? [];
|
||||||
|
const scenarioVariables = scenario.variableValues as Record<string, string>;
|
||||||
|
const templateHasVariables =
|
||||||
|
experimentVariables.length === 0 ||
|
||||||
|
experimentVariables.some((v) => scenarioVariables[v] !== undefined);
|
||||||
|
|
||||||
|
const output = api.outputs.get.useQuery(
|
||||||
|
{
|
||||||
scenarioId: scenario.id,
|
scenarioId: scenario.id,
|
||||||
variantId: variant.id,
|
variantId: variant.id,
|
||||||
});
|
},
|
||||||
|
{ enabled: templateHasVariables }
|
||||||
|
);
|
||||||
|
|
||||||
if (!output.data) return null;
|
if (!templateHasVariables)
|
||||||
|
return (
|
||||||
|
<Center h="100%">
|
||||||
|
<Text color="gray.500">Add a scenario variable to see output</Text>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (output.isLoading) return <Center h="100%">Loading...</Center>;
|
||||||
|
|
||||||
|
if (!output.data) return <Center h="100%">No output</Center>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Center h="100%">
|
<Center h="100%">
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { isEqual } from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import { PromptVariant, Scenario } from "./types";
|
import { type Scenario } from "./types";
|
||||||
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Badge, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react";
|
import ResizeTextarea from "react-textarea-autosize";
|
||||||
|
|
||||||
|
import { Box, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react";
|
||||||
|
|
||||||
export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
||||||
const savedValues = scenario.variableValues as Record<string, string>;
|
const savedValues = scenario.variableValues as Record<string, string>;
|
||||||
@@ -30,34 +32,53 @@ export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
|||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
{variableLabels.map((key) => {
|
{variableLabels.map((key) => {
|
||||||
|
const value = values[key] ?? "";
|
||||||
|
const layoutDirection = value.length > 20 ? "column" : "row";
|
||||||
return (
|
return (
|
||||||
<Flex key={key}>
|
<Flex
|
||||||
<Badge>{key}</Badge>
|
|
||||||
<Textarea
|
|
||||||
key={key}
|
key={key}
|
||||||
value={values[key] ?? ""}
|
direction={layoutDirection}
|
||||||
|
alignItems={layoutDirection === "column" ? "flex-start" : "center"}
|
||||||
|
flexWrap="wrap"
|
||||||
|
>
|
||||||
|
<Box bgColor="blue.100" color="blue.600" px={2} fontSize="xs" fontWeight="bold">
|
||||||
|
{key}
|
||||||
|
</Box>
|
||||||
|
<Textarea
|
||||||
|
borderRadius={0}
|
||||||
|
px={2}
|
||||||
|
py={1}
|
||||||
|
placeholder="empty"
|
||||||
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValues((prev) => ({ ...prev, [key]: e.target.value }));
|
setValues((prev) => ({ ...prev, [key]: e.target.value }));
|
||||||
}}
|
}}
|
||||||
rows={1}
|
resize="none"
|
||||||
// TODO: autosize
|
overflow="hidden"
|
||||||
maxRows={20}
|
minRows={1}
|
||||||
|
minH="unset"
|
||||||
|
as={ResizeTextarea}
|
||||||
|
flex={layoutDirection === "row" ? 1 : undefined}
|
||||||
|
borderColor={hasChanged ? "blue.300" : "transparent"}
|
||||||
|
_hover={{ borderColor: "gray.300" }}
|
||||||
|
_focus={{ borderColor: "blue.500", outline: "none" }}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{hasChanged && (
|
{hasChanged && (
|
||||||
<HStack spacing={4}>
|
<HStack justify="right">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="sm"
|
||||||
onClick={() => {
|
borderRadius={0}
|
||||||
|
onMouseDown={() => {
|
||||||
setValues(savedValues);
|
setValues(savedValues);
|
||||||
}}
|
}}
|
||||||
color="gray"
|
colorScheme="gray"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="xs" onClick={onSave}>
|
<Button size="sm" borderRadius={0} onMouseDown={onSave} colorScheme="blue">
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -108,19 +108,20 @@ export default function VariantConfigEditor(props: {
|
|||||||
<Box w="100%" pos="relative">
|
<Box w="100%" pos="relative">
|
||||||
<div id={editorId} style={{ height: "300px", width: "100%" }}></div>
|
<div id={editorId} style={{ height: "300px", width: "100%" }}></div>
|
||||||
{isChanged && (
|
{isChanged && (
|
||||||
<HStack pos="absolute" bottom={0} right={0} spacing={4}>
|
<HStack pos="absolute" bottom={0} right={0}>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="gray"
|
colorScheme="gray"
|
||||||
size="xs"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
editorRef.current?.setValue(props.savedConfig);
|
editorRef.current?.setValue(props.savedConfig);
|
||||||
checkForChanges();
|
checkForChanges();
|
||||||
}}
|
}}
|
||||||
|
borderRadius={0}
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip label={`${modifierKey} + Enter`}>
|
<Tooltip label={`${modifierKey} + Enter`}>
|
||||||
<Button size="xs" onClick={onSave} colorScheme="blue">
|
<Button size="sm" onClick={onSave} colorScheme="blue" borderRadius={0}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -21,17 +21,15 @@ export default function VariantHeader({ variant }: { variant: PromptVariant }) {
|
|||||||
title: "Invalid JSON",
|
title: "Invalid JSON",
|
||||||
description: "Please fix the JSON before saving.",
|
description: "Please fix the JSON before saving.",
|
||||||
status: "error",
|
status: "error",
|
||||||
duration: 5000,
|
|
||||||
position: "top",
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedConfig === null) {
|
if (parsedConfig === null) {
|
||||||
notifications.show({
|
toast({
|
||||||
title: "Invalid JSON",
|
title: "Invalid JSON",
|
||||||
message: "Please fix the JSON before saving.",
|
description: "Please fix the JSON before saving.",
|
||||||
color: "red",
|
status: "error",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -43,7 +41,7 @@ export default function VariantHeader({ variant }: { variant: PromptVariant }) {
|
|||||||
|
|
||||||
await utils.promptVariants.list.invalidate();
|
await utils.promptVariants.list.invalidate();
|
||||||
},
|
},
|
||||||
[variant.id, replaceWithConfig, utils.promptVariants.list]
|
[variant.id, replaceWithConfig, utils.promptVariants.list, toast]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import { RouterOutputs, api } from "~/utils/api";
|
import { RouterOutputs, api } from "~/utils/api";
|
||||||
import { type PromptVariant } from "./types";
|
import { type PromptVariant } from "./types";
|
||||||
import VariantHeader from "./VariantHeader";
|
import VariantHeader from "./VariantHeader";
|
||||||
import OutputCell from "./OutputCell";
|
import OutputCell from "./OutputCell";
|
||||||
import ScenarioHeader from "./ScenarioHeader";
|
import ScenarioHeader from "./ScenarioHeader";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Box, Heading } from "@chakra-ui/react";
|
import { Box, Grid, GridItem, Heading } from "@chakra-ui/react";
|
||||||
|
import NewScenarioButton from "./NewScenarioButton";
|
||||||
const cellPaddingX = 4;
|
|
||||||
const cellPaddingY = 2;
|
|
||||||
|
|
||||||
export default function OutputsTable({ experimentId }: { experimentId: string | undefined }) {
|
export default function OutputsTable({ experimentId }: { experimentId: string | undefined }) {
|
||||||
const variants = api.promptVariants.list.useQuery(
|
const variants = api.promptVariants.list.useQuery(
|
||||||
@@ -24,37 +21,48 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
if (!variants.data || !scenarios.data) return null;
|
if (!variants.data || !scenarios.data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box p={4}>
|
<Grid
|
||||||
<div
|
p={4}
|
||||||
style={{
|
display="grid"
|
||||||
display: "grid",
|
gridTemplateColumns={`200px repeat(${variants.data.length}, minmax(300px, 1fr))`}
|
||||||
gridTemplateColumns: `200px repeat(${variants.data.length}, minmax(300px, 1fr))`,
|
overflowX="auto"
|
||||||
overflowX: "auto",
|
sx={{
|
||||||
|
"> *": {
|
||||||
|
borderColor: "gray.300",
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
paddingX: 4,
|
||||||
|
paddingY: 2,
|
||||||
|
},
|
||||||
|
"> *:last-child": {
|
||||||
|
borderRightWidth: 0,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box px={cellPaddingX} py={cellPaddingY} display="flex" sx={{}}>
|
<GridItem display="flex" alignItems="flex-end">
|
||||||
<Heading size="md" fontWeight="bold">
|
<Heading size="md" fontWeight="bold">
|
||||||
Scenario
|
Scenario
|
||||||
</Heading>
|
</Heading>
|
||||||
</Box>
|
</GridItem>
|
||||||
{variants.data.map((variant) => (
|
{variants.data.map((variant) => (
|
||||||
<Box key={variant.uiId} px={cellPaddingX} py={cellPaddingY}>
|
<GridItem key={variant.uiId}>
|
||||||
<VariantHeader key={variant.uiId} variant={variant} />
|
<VariantHeader key={variant.uiId} variant={variant} />
|
||||||
</Box>
|
</GridItem>
|
||||||
))}
|
))}
|
||||||
{scenarios.data.map((scenario) => (
|
{scenarios.data.map((scenario) => (
|
||||||
<React.Fragment key={scenario.uiId}>
|
<React.Fragment key={scenario.uiId}>
|
||||||
<Box px={cellPaddingX} py={cellPaddingY}>
|
<GridItem>
|
||||||
<ScenarioHeader scenario={scenario} />
|
<ScenarioHeader scenario={scenario} />
|
||||||
</Box>
|
</GridItem>
|
||||||
{variants.data.map((variant) => (
|
{variants.data.map((variant) => (
|
||||||
<Box key={variant.id} px={cellPaddingX} py={cellPaddingY}>
|
<GridItem key={variant.id}>
|
||||||
<OutputCell key={variant.id} scenario={scenario} variant={variant} />
|
<OutputCell key={variant.id} scenario={scenario} variant={variant} />
|
||||||
</Box>
|
</GridItem>
|
||||||
))}
|
))}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
<GridItem borderBottomWidth={0} w="100%" colSpan={variants.data.length + 1} px={0} py={0}>
|
||||||
</Box>
|
<NewScenarioButton />
|
||||||
|
</GridItem>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { SessionProvider } from "next-auth/react";
|
|||||||
import { type AppType } from "next/app";
|
import { type AppType } from "next/app";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { ChakraProvider } from "@chakra-ui/react";
|
import { ChakraProvider } from "@chakra-ui/react";
|
||||||
|
import theme from "~/utils/theme";
|
||||||
|
|
||||||
const MyApp: AppType<{ session: Session | null }> = ({
|
const MyApp: AppType<{ session: Session | null }> = ({
|
||||||
Component,
|
Component,
|
||||||
@@ -10,7 +11,7 @@ const MyApp: AppType<{ session: Session | null }> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<ChakraProvider>
|
<ChakraProvider theme={theme}>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function Experiment() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AppNav title={experiment.data?.label}>
|
<AppNav title={experiment.data?.label}>
|
||||||
<Box sx={{ minHeight: "100vh" }}>
|
<Box minH="100vh" mb={50}>
|
||||||
<OutputsTable experimentId={router.query.id as string | undefined} />
|
<OutputsTable experimentId={router.query.id as string | undefined} />
|
||||||
</Box>
|
</Box>
|
||||||
</AppNav>
|
</AppNav>
|
||||||
|
|||||||
@@ -15,6 +15,34 @@ export const scenariosRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
create: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
experimentId: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const maxSortIndex =
|
||||||
|
(
|
||||||
|
await prisma.testScenario.aggregate({
|
||||||
|
where: {
|
||||||
|
experimentId: input.experimentId,
|
||||||
|
},
|
||||||
|
_max: {
|
||||||
|
sortIndex: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)._max.sortIndex ?? 0;
|
||||||
|
|
||||||
|
const newScenario = await prisma.testScenario.create({
|
||||||
|
data: {
|
||||||
|
experimentId: input.experimentId,
|
||||||
|
sortIndex: maxSortIndex + 1,
|
||||||
|
variableValues: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
replaceWithValues: publicProcedure
|
replaceWithValues: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|||||||
12
src/utils/theme.ts
Normal file
12
src/utils/theme.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { extendTheme } from "@chakra-ui/react";
|
||||||
|
import "@fontsource/poppins";
|
||||||
|
import "@fontsource/roboto";
|
||||||
|
|
||||||
|
const theme = extendTheme({
|
||||||
|
fonts: {
|
||||||
|
heading: "Poppins, sans-serif",
|
||||||
|
body: "Roboto, sans-serif",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default theme;
|
||||||
Reference in New Issue
Block a user