import { Text, Button, HStack, Heading, Icon, IconButton, Stack, VStack } from "@chakra-ui/react"; import { type TemplateVariable } from "@prisma/client"; import { useEffect, useState } from "react"; import { BsPencil, BsX } from "react-icons/bs"; import { api } from "~/utils/api"; import { useExperiment, useHandledAsyncCallback, useScenarioVars } from "~/utils/hooks"; import { maybeReportError } from "~/utils/errorHandling/maybeReportError"; import { FloatingLabelInput } from "./FloatingLabelInput"; export const ScenarioVar = ({ variable, isEditing, setIsEditing, }: { variable: Pick; isEditing: boolean; setIsEditing: (isEditing: boolean) => void; }) => { const utils = api.useContext(); const [label, setLabel] = useState(variable.label); useEffect(() => { setLabel(variable.label); }, [variable.label]); const renameVarMutation = api.scenarioVars.rename.useMutation(); const [onRename] = useHandledAsyncCallback(async () => { const resp = await renameVarMutation.mutateAsync({ id: variable.id, label }); if (maybeReportError(resp)) return; setIsEditing(false); await utils.scenarioVars.list.invalidate(); await utils.scenarios.list.invalidate(); }, [label, variable.id]); const deleteMutation = api.scenarioVars.delete.useMutation(); const [onDeleteVar] = useHandledAsyncCallback(async () => { await deleteMutation.mutateAsync({ id: variable.id }); await utils.scenarioVars.list.invalidate(); }, [variable.id]); if (isEditing) { return ( setLabel(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); onRename(); } // If the user types a space, replace it with an underscore if (e.key === " ") { e.preventDefault(); setLabel((label) => label && `${label}_`); } }} /> ); } else { return ( {variable.label} setIsEditing(true)} _hover={{ color: "gray.800", cursor: "pointer" }} icon={} /> } /> ); } }; export default function EditScenarioVars() { const experiment = useExperiment(); const vars = useScenarioVars(); const [currentlyEditingId, setCurrentlyEditingId] = useState(null); const [newVariable, setNewVariable] = useState(""); const newVarIsValid = newVariable?.length ?? 0 > 0; const utils = api.useContext(); const addVarMutation = api.scenarioVars.create.useMutation(); const [onAddVar] = useHandledAsyncCallback(async () => { if (!experiment.data?.id) return; if (!newVariable) return; const resp = await addVarMutation.mutateAsync({ experimentId: experiment.data.id, label: newVariable, }); if (maybeReportError(resp)) return; await utils.scenarioVars.list.invalidate(); setNewVariable(""); }, [addVarMutation, experiment.data?.id, newVarIsValid, newVariable]); return ( Scenario Variables Scenario variables can be used in your prompt variants as well as evaluations. {vars.data?.map((variable) => ( { if (isEditing) { setCurrentlyEditingId(variable.id); } else { setCurrentlyEditingId(null); } }} /> ))} {currentlyEditingId !== "new" && ( )} {currentlyEditingId === "new" && ( setNewVariable(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); onAddVar(); } // If the user types a space, replace it with an underscore if (e.key === " ") { e.preventDefault(); setNewVariable((v) => v && `${v}_`); } }} /> )} ); }