editing scenarios is kinda working

This commit is contained in:
Kyle Corbitt
2023-06-23 16:18:28 -07:00
parent bf41069442
commit 2b0c2ad603
12 changed files with 221 additions and 35 deletions

View File

@@ -0,0 +1,27 @@
import { useRef } from "react";
import { Title } from "@mantine/core";
import { type PromptVariant } from "./types";
import { api } from "~/utils/api";
import { useHandledAsyncCallback } from "~/utils/hooks";
export default function EditableVariantLabel(props: { variant: PromptVariant }) {
const labelRef = useRef<HTMLHeadingElement | null>(null);
const mutation = api.promptVariants.update.useMutation();
const [onBlur] = useHandledAsyncCallback(async () => {
const newLabel = labelRef.current?.innerText;
if (newLabel && newLabel !== props.variant.label) {
await mutation.mutateAsync({
id: props.variant.id,
updates: { label: newLabel },
});
}
}, [mutation, props.variant.id, props.variant.label]);
return (
<Title order={4} ref={labelRef} contentEditable suppressContentEditableWarning onBlur={onBlur}>
{props.variant.label}
</Title>
);
}

View File

@@ -0,0 +1,65 @@
import { api } from "~/utils/api";
import { isEqual } from "lodash";
import { PromptVariant, Scenario } from "./types";
import { Badge, Button, Group, Stack, TextInput, Textarea, Tooltip } from "@mantine/core";
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
import { useState } from "react";
export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
const savedValues = scenario.variableValues as Record<string, string>;
const utils = api.useContext();
const [values, setValues] = useState<Record<string, string>>(savedValues);
const experiment = useExperiment();
const variableLabels = experiment.data?.TemplateVariable.map((v) => v.label) ?? [];
const hasChanged = !isEqual(savedValues, values);
const mutation = api.scenarios.replaceWithValues.useMutation();
const [onSave] = useHandledAsyncCallback(async () => {
await mutation.mutateAsync({
id: scenario.id,
values,
});
await utils.scenarios.list.invalidate();
}, [mutation, values]);
return (
<Stack>
{variableLabels.map((key) => {
return (
<Textarea
key={key}
label={key}
value={values[key] ?? ""}
onChange={(e) => {
setValues((prev) => ({ ...prev, [key]: e.target.value }));
}}
autosize
rows={1}
maxRows={20}
/>
);
})}
{hasChanged && (
<Group spacing={4} position="right">
<Button
size="xs"
onClick={() => {
setValues(savedValues);
}}
color="gray"
>
Reset
</Button>
<Button size="xs" onClick={onSave}>
Save
</Button>
</Group>
)}
</Stack>
);
}

View File

@@ -1,8 +1,8 @@
import { Box, Button, Group, Stack, Title } from "@mantine/core";
import { Box, Button, Group, Stack, Title, Tooltip } from "@mantine/core";
import { useMonaco } from "@monaco-editor/react";
import { useRef, useEffect, useState, useCallback } from "react";
import { set } from "zod";
import { useHandledAsyncCallback } from "~/utils/hooks";
import { useHandledAsyncCallback, useModifierKeyLabel } from "~/utils/hooks";
let isThemeDefined = false;
@@ -16,6 +16,8 @@ export default function VariantConfigEditor(props: {
const [isChanged, setIsChanged] = useState(false);
const savedConfigRef = useRef(props.savedConfig);
const modifierKey = useModifierKeyLabel();
const checkForChanges = useCallback(() => {
if (!editorRef.current) return;
const currentConfig = editorRef.current.getValue();
@@ -104,9 +106,11 @@ export default function VariantConfigEditor(props: {
>
Reset
</Button>
<Button size="xs" onClick={onSave}>
Save
</Button>
<Tooltip label={`${modifierKey} + Enter`} withArrow>
<Button size="xs" onClick={onSave}>
Save
</Button>
</Tooltip>
</Group>
)}
</Box>

View File

@@ -5,6 +5,7 @@ import { api } from "~/utils/api";
import { notifications } from "@mantine/notifications";
import { type JSONSerializable } from "~/server/types";
import VariantConfigEditor from "./VariantConfigEditor";
import EditableVariantLabel from "./EditableVariantLabel";
export default function VariantHeader({ variant }: { variant: PromptVariant }) {
const replaceWithConfig = api.promptVariants.replaceWithConfig.useMutation();
@@ -39,15 +40,14 @@ export default function VariantHeader({ variant }: { variant: PromptVariant }) {
});
await utils.promptVariants.list.invalidate();
// TODO: invalidate the variants query
},
[variant.id, replaceWithConfig, utils.promptVariants.list]
);
return (
<Stack w="100%">
<Title order={4}>{variant.label}</Title>
// title="" to hide the title text that mantine-react-table likes to add
<Stack w="100%" title="">
<EditableVariantLabel variant={variant} />
<VariantConfigEditor savedConfig={JSON.stringify(variant.config, null, 2)} onSave={onSave} />
</Stack>
);

View File

@@ -4,6 +4,7 @@ import { RouterOutputs, api } from "~/utils/api";
import { PromptVariant } from "./types";
import VariantHeader from "./VariantHeader";
import OutputCell from "./OutputCell";
import ScenarioHeader from "./ScenarioHeader";
type CellData = {
variant: PromptVariant;
@@ -15,11 +16,6 @@ type TableRow = {
} & Record<string, CellData>;
export default function OutputsTable({ experimentId }: { experimentId: string | undefined }) {
const experiment = api.experiments.get.useQuery(
{ id: experimentId as string },
{ enabled: !!experimentId }
);
const variants = api.promptVariants.list.useQuery(
{ experimentId: experimentId as string },
{ enabled: !!experimentId }
@@ -37,9 +33,7 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
header: "Scenario",
enableColumnDragging: false,
size: 200,
Cell: ({ row }) => {
return <div>{JSON.stringify(row.original.scenario.variableValues)}</div>;
},
Cell: ({ row }) => <ScenarioHeader scenario={row.original.scenario} />,
},
...(variants.data?.map(
(variant): MRT_ColumnDef<TableRow> => ({
@@ -91,7 +85,6 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
"& .mantine-TableHeadCell-Content": {
width: "100%",
height: "100%",
// display: "flex",
"& .mantine-TableHeadCell-Content-Actions": {
alignSelf: "flex-start",