import React, { useEffect, useState, useRef, type ReactNode } from "react"; import { CircleFadingArrowUp, PlusIcon } from "lucide-react"; import { Button } from "@/src/components/ui/button"; import { InputCommand, InputCommandGroup, InputCommandList, InputCommandSeparator, } from "@/src/components/ui/input-command"; import { Popover, PopoverContent, PopoverTrigger, } from "@/src/components/ui/popover"; import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess"; import useProjectIdFromURL from "@/src/hooks/useProjectIdFromURL"; import { api } from "@/src/utils/api"; import { PRODUCTION_LABEL, type Prompt } from "@langfuse/shared"; import { AddLabelForm } from "./AddLabelForm"; import { LabelCommandItem } from "./LabelCommandItem"; import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture"; import { isReservedPromptLabel } from "@/src/features/prompts/utils"; import { TruncatedLabels } from "@/src/components/TruncatedLabels"; import { cn } from "@/src/utils/tailwind"; export function SetPromptVersionLabels({ promptLabels, prompt, isOpen, setIsOpen, title, showOnlyOnHover = false, maxVisibleLabels = 8, }: { promptLabels: string[]; prompt: Prompt; isOpen: boolean; setIsOpen: (isOpen: boolean) => void; title?: ReactNode; showOnlyOnHover?: boolean; maxVisibleLabels?: number; }) { const projectId = useProjectIdFromURL(); const utils = api.useUtils(); const capture = usePostHogClientCapture(); const hasAccess = useHasProjectAccess({ projectId, scope: "prompts:CUD" }); const [selectedLabels, setSelectedLabels] = useState([]); const [labels, setLabels] = useState([]); const [isAddingLabel, setIsAddingLabel] = useState(false); const labelsChanged = JSON.stringify([...selectedLabels].sort()) !== JSON.stringify([...prompt.labels].sort()); const customLabelScrollRef = useRef(null); const usedLabelsInProject = api.prompts.allLabels.useQuery( { projectId: projectId as string, // Typecast as query is enabled only when projectId is present }, { enabled: Boolean(projectId) }, ); // Set initial labels and selected labels useEffect(() => { if (isOpen) { setLabels([ ...new Set([...prompt.labels, ...(usedLabelsInProject.data ?? [])]), ]); setSelectedLabels(prompt.labels); } }, [isOpen, prompt.labels, usedLabelsInProject.data]); const isPromotingToProduction = !prompt.labels.includes(PRODUCTION_LABEL) && selectedLabels.includes(PRODUCTION_LABEL); const isDemotingFromProduction = prompt.labels.includes(PRODUCTION_LABEL) && !selectedLabels.includes(PRODUCTION_LABEL); const mutatePromptVersionLabels = api.prompts.setLabels.useMutation({ onSuccess: () => { void utils.prompts.invalidate(); }, }); const handleSubmitLabels = async () => { try { if (!projectId) { alert("Project ID is missing"); return; } await mutatePromptVersionLabels.mutateAsync({ projectId: projectId as string, promptId: prompt.id, labels: selectedLabels, }); capture("prompt_detail:apply_labels", { labels: selectedLabels }); setIsOpen(false); } catch (err) { console.error(err); } }; const handleOnOpenChange = (open: boolean) => { if (!hasAccess) setIsOpen(false); else setIsOpen(open); }; return (
{title && title}
event.stopPropagation()} className="flex flex-col" >

Prompt labels

Use labels to fetch prompts via SDKs. The{" "} production labeled prompt will be served by default.

{labels .filter((l) => !isReservedPromptLabel(l)) .map((label) => ( ))}
{isAddingLabel ? ( { setTimeout( () => customLabelScrollRef.current?.scrollTo({ top: customLabelScrollRef.current?.scrollHeight, behavior: "smooth", }), 0, ); }, }} /> ) : ( )}
); }