Make it so you can't delete the last prompt or scenario
No reason for an experiment to have 0 prompts or 0 scenarios and it makes the UI look bad.
This commit is contained in:
@@ -13,10 +13,11 @@ import AutoResizeTextArea from "../AutoResizeTextArea";
|
||||
|
||||
export default function ScenarioEditor({
|
||||
scenario,
|
||||
hovered,
|
||||
...props
|
||||
}: {
|
||||
scenario: Scenario;
|
||||
hovered: boolean;
|
||||
canHide: boolean;
|
||||
}) {
|
||||
const savedValues = scenario.variableValues as Record<string, string>;
|
||||
const utils = api.useContext();
|
||||
@@ -92,30 +93,34 @@ export default function ScenarioEditor({
|
||||
onDrop={onReorder}
|
||||
backgroundColor={isDragTarget ? "gray.100" : "transparent"}
|
||||
>
|
||||
<Stack alignSelf="flex-start" opacity={hovered ? 1 : 0} spacing={0}>
|
||||
<Tooltip label="Hide scenario" hasArrow>
|
||||
{/* for some reason the tooltip can't position itself properly relative to the icon without the wrapping box */}
|
||||
<Button
|
||||
variant="unstyled"
|
||||
color="gray.400"
|
||||
height="unset"
|
||||
width="unset"
|
||||
minW="unset"
|
||||
onClick={onHide}
|
||||
_hover={{
|
||||
color: "gray.800",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<Icon as={hidingInProgress ? Spinner : BsX} boxSize={6} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Icon
|
||||
as={RiDraggable}
|
||||
boxSize={6}
|
||||
color="gray.400"
|
||||
_hover={{ color: "gray.800", cursor: "pointer" }}
|
||||
/>
|
||||
<Stack alignSelf="flex-start" opacity={props.hovered ? 1 : 0} spacing={0}>
|
||||
{props.canHide && (
|
||||
<>
|
||||
<Tooltip label="Hide scenario" hasArrow>
|
||||
{/* for some reason the tooltip can't position itself properly relative to the icon without the wrapping box */}
|
||||
<Button
|
||||
variant="unstyled"
|
||||
color="gray.400"
|
||||
height="unset"
|
||||
width="unset"
|
||||
minW="unset"
|
||||
onClick={onHide}
|
||||
_hover={{
|
||||
color: "gray.800",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<Icon as={hidingInProgress ? Spinner : BsX} boxSize={6} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Icon
|
||||
as={RiDraggable}
|
||||
boxSize={6}
|
||||
color="gray.400"
|
||||
_hover={{ color: "gray.800", cursor: "pointer" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
{variableLabels.length === 0 ? (
|
||||
<Box color="gray.500">{vars.data ? "No scenario variables configured" : "Loading..."}</Box>
|
||||
|
||||
@@ -5,7 +5,11 @@ import OutputCell from "./OutputCell/OutputCell";
|
||||
import ScenarioEditor from "./ScenarioEditor";
|
||||
import type { PromptVariant, Scenario } from "./types";
|
||||
|
||||
const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) => {
|
||||
const ScenarioRow = (props: {
|
||||
scenario: Scenario;
|
||||
variants: PromptVariant[];
|
||||
canHide: boolean;
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const highlightStyle = { backgroundColor: "gray.50" };
|
||||
@@ -18,7 +22,7 @@ const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) =
|
||||
sx={isHovered ? highlightStyle : undefined}
|
||||
borderLeftWidth={1}
|
||||
>
|
||||
<ScenarioEditor scenario={props.scenario} hovered={isHovered} />
|
||||
<ScenarioEditor scenario={props.scenario} hovered={isHovered} canHide={props.canHide} />
|
||||
</GridItem>
|
||||
{props.variants.map((variant) => (
|
||||
<GridItem
|
||||
|
||||
@@ -8,7 +8,7 @@ import { RiDraggable } from "react-icons/ri";
|
||||
import { cellPadding, headerMinHeight } from "../constants";
|
||||
import AutoResizeTextArea from "../AutoResizeTextArea";
|
||||
|
||||
export default function VariantHeader(props: { variant: PromptVariant }) {
|
||||
export default function VariantHeader(props: { variant: PromptVariant; canHide: boolean }) {
|
||||
const utils = api.useContext();
|
||||
const [isDragTarget, setIsDragTarget] = useState(false);
|
||||
const [isInputHovered, setIsInputHovered] = useState(false);
|
||||
@@ -95,11 +95,13 @@ export default function VariantHeader(props: { variant: PromptVariant }) {
|
||||
onMouseEnter={() => setIsInputHovered(true)}
|
||||
onMouseLeave={() => setIsInputHovered(false)}
|
||||
/>
|
||||
<Tooltip label="Hide Variant" hasArrow>
|
||||
<Button variant="ghost" colorScheme="gray" size="sm" onClick={onHide}>
|
||||
<Icon as={BsX} boxSize={6} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{props.canHide && (
|
||||
<Tooltip label="Remove Variant" hasArrow>
|
||||
<Button variant="ghost" colorScheme="gray" size="sm" onClick={onHide}>
|
||||
<Icon as={BsX} boxSize={6} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
||||
|
||||
{variants.data.map((variant) => (
|
||||
<GridItem key={variant.uiId} padding={0} sx={stickyHeaderStyle} borderTopWidth={1}>
|
||||
<VariantHeader variant={variant} />
|
||||
<VariantHeader variant={variant} canHide={variants.data.length > 1} />
|
||||
</GridItem>
|
||||
))}
|
||||
<GridItem
|
||||
@@ -103,7 +103,12 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
||||
</GridItem>
|
||||
))}
|
||||
{scenarios.data.map((scenario) => (
|
||||
<ScenarioRow key={scenario.uiId} scenario={scenario} variants={variants.data} />
|
||||
<ScenarioRow
|
||||
key={scenario.uiId}
|
||||
scenario={scenario}
|
||||
variants={variants.data}
|
||||
canHide={scenarios.data.length > 1}
|
||||
/>
|
||||
))}
|
||||
<GridItem borderBottomWidth={0} borderRightWidth={0} w="100%" colSpan={allCols} padding={0}>
|
||||
<NewScenarioButton />
|
||||
|
||||
@@ -74,15 +74,23 @@ export const experimentsRouter = createTRPCRouter({
|
||||
constructFn: dedent`prompt = {
|
||||
model: "gpt-3.5-turbo-0613",
|
||||
stream: true,
|
||||
messages: [{ role: "system", content: "Return 'Ready to go!'" }],
|
||||
messages: [{ role: "system", content: ${"`Return '${scenario.text}'`"} }],
|
||||
}`,
|
||||
model: "gpt-3.5-turbo-0613",
|
||||
},
|
||||
}),
|
||||
prisma.templateVariable.create({
|
||||
data: {
|
||||
experimentId: exp.id,
|
||||
label: "text",
|
||||
},
|
||||
}),
|
||||
prisma.testScenario.create({
|
||||
data: {
|
||||
experimentId: exp.id,
|
||||
variableValues: {},
|
||||
variableValues: {
|
||||
text: "This is a test scenario.",
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import dedent from "dedent";
|
||||
import { isObject } from "lodash";
|
||||
import { z } from "zod";
|
||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||
@@ -113,7 +114,18 @@ export const promptVariantsRouter = createTRPCRouter({
|
||||
experimentId: input.experimentId,
|
||||
label: `Prompt Variant ${largestSortIndex + 2}`,
|
||||
sortIndex: (lastVariant?.sortIndex ?? 0) + 1,
|
||||
constructFn: lastVariant?.constructFn ?? "",
|
||||
constructFn:
|
||||
lastVariant?.constructFn ??
|
||||
dedent`
|
||||
prompt = {
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Return 'Hello, world!'",
|
||||
}
|
||||
]
|
||||
}`,
|
||||
model: lastVariant?.model ?? "gpt-3.5-turbo",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function constructPrompt(
|
||||
scenario: TestScenario["variableValues"],
|
||||
): Promise<JSONSerializable> {
|
||||
const code = `
|
||||
const scenario = ${JSON.stringify(scenario, null, 2)};
|
||||
const scenario = ${JSON.stringify(scenario ?? {}, null, 2)};
|
||||
let prompt
|
||||
|
||||
${variant.constructFn}
|
||||
|
||||
@@ -33,6 +33,7 @@ export const createVariantEditorSlice: SliceCreator<SharedVariantEditorSlice> =
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
allowNonTsExtensions: true,
|
||||
strictNullChecks: true,
|
||||
lib: ["esnext"],
|
||||
});
|
||||
|
||||
@@ -84,7 +85,7 @@ export const createVariantEditorSlice: SliceCreator<SharedVariantEditorSlice> =
|
||||
)} as const;
|
||||
|
||||
type Scenario = typeof scenarios[number];
|
||||
declare var scenario: Scenario | null;
|
||||
declare var scenario: Scenario | { [key: string]: string };
|
||||
`;
|
||||
|
||||
const scenariosModel = monaco.editor.getModel(monaco.Uri.parse("file:///scenarios.ts"));
|
||||
|
||||
Reference in New Issue
Block a user