Skip to main content
Glama

@arizeai/phoenix-mcp

Official
by Arize-ai
ExperimentActionMenu.tsx8.22 kB
import { useCallback, useState } from "react"; import { graphql, useMutation } from "react-relay"; import { useNavigate } from "react-router"; import copy from "copy-to-clipboard"; import { Button, Dialog, Flex, Icon, Icons, Menu, MenuItem, MenuTrigger, Modal, ModalOverlay, Popover, Text, View, } from "@phoenix/components"; import { JSONBlock } from "@phoenix/components/code"; import { DialogCloseButton, DialogContent, DialogHeader, DialogTitle, DialogTitleExtra, } from "@phoenix/components/dialog"; import { StopPropagation } from "@phoenix/components/StopPropagation"; import { useNotifyError, useNotifySuccess } from "@phoenix/contexts"; import { assertUnreachable } from "@phoenix/typeUtils"; import { getErrorMessagesFromRelayMutationError } from "@phoenix/utils/errorUtils"; export enum ExperimentAction { GO_TO_EXPERIMENT_RUN_TRACES = "GO_TO_EXPERIMENT_RUN_TRACES", COPY_EXPERIMENT_ID = "COPY_EXPERIMENT_ID", VIEW_METADATA = "VIEW_METADATA", DELETE_EXPERIMENT = "DELETE_EXPERIMENT", } type ExperimentActionMenuProps = | { projectId?: string | null; experimentId: string; metadata: unknown; canDeleteExperiment: true; onExperimentDeleted: () => void; } | { projectId?: string | null; experimentId: string; metadata: unknown; canDeleteExperiment: false; onExperimentDeleted?: undefined; }; export function ExperimentActionMenu(props: ExperimentActionMenuProps) { const [commitDeleteExperiment, isDeletingExperiment] = useMutation(graphql` mutation ExperimentActionMenuDeleteExperimentMutation( $input: DeleteExperimentsInput! ) { deleteExperiments(input: $input) { __typename } } `); const { projectId } = props; const navigate = useNavigate(); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [isMetadataDialogOpen, setIsMetadataDialogOpen] = useState(false); const notifySuccess = useNotifySuccess(); const notifyError = useNotifyError(); const onExperimentDeleted = props.onExperimentDeleted; const onDeleteExperiment = useCallback( (experimentId: string) => { commitDeleteExperiment({ variables: { input: { experimentIds: [experimentId], }, }, onCompleted: () => { notifySuccess({ title: "Experiment deleted", message: `The experiment has been deleted.`, }); }, onError: (error) => { const formattedError = getErrorMessagesFromRelayMutationError(error); notifyError({ title: "An error occurred", message: `Failed to delete experiment: ${formattedError?.[0] ?? error.message}`, }); }, }); onExperimentDeleted?.(); setIsDeleteDialogOpen(false); }, [commitDeleteExperiment, notifySuccess, notifyError, onExperimentDeleted] ); const menuItems = [ <MenuItem key={ExperimentAction.GO_TO_EXPERIMENT_RUN_TRACES} id={ExperimentAction.GO_TO_EXPERIMENT_RUN_TRACES} > <Flex direction="row" gap="size-75" justifyContent="start" alignItems="center" > <Icon svg={<Icons.Trace />} /> <Text>View run traces</Text> </Flex> </MenuItem>, <MenuItem key={ExperimentAction.VIEW_METADATA} id={ExperimentAction.VIEW_METADATA} > <Flex direction="row" gap="size-75" justifyContent="start" alignItems="center" > <Icon svg={<Icons.InfoOutline />} /> <Text>View metadata</Text> </Flex> </MenuItem>, <MenuItem key={ExperimentAction.COPY_EXPERIMENT_ID} id={ExperimentAction.COPY_EXPERIMENT_ID} > <Flex direction="row" gap="size-75" justifyContent="start" alignItems="center" > <Icon svg={<Icons.ClipboardCopy />} /> <Text>Copy experiment ID</Text> </Flex> </MenuItem>, ]; if (props.canDeleteExperiment) { menuItems.push( <MenuItem key={ExperimentAction.DELETE_EXPERIMENT} id={ExperimentAction.DELETE_EXPERIMENT} > <Flex direction="row" gap="size-75" justifyContent="start" alignItems="center" > <Icon svg={<Icons.TrashOutline />} /> <Text>{isDeletingExperiment ? "Deleting..." : "Delete"}</Text> </Flex> </MenuItem> ); } return ( <StopPropagation> <MenuTrigger> <Button aria-label="Experiment action menu" leadingVisual={<Icon svg={<Icons.MoreHorizontalOutline />} />} size="S" /> <Popover> <Menu disabledKeys={ projectId ? [] : [ExperimentAction.GO_TO_EXPERIMENT_RUN_TRACES] } onAction={(firedAction) => { const action = firedAction as ExperimentAction; switch (action) { case ExperimentAction.GO_TO_EXPERIMENT_RUN_TRACES: { return navigate(`/projects/${projectId}`); } case ExperimentAction.VIEW_METADATA: { setIsMetadataDialogOpen(true); break; } case ExperimentAction.COPY_EXPERIMENT_ID: { copy(props.experimentId); notifySuccess({ title: "Copied", message: "The experiment ID has been copied to your clipboard", }); break; } case ExperimentAction.DELETE_EXPERIMENT: { setIsDeleteDialogOpen(true); break; } default: { assertUnreachable(action); } } }} > {menuItems} </Menu> </Popover> </MenuTrigger> <ModalOverlay isDismissable isOpen={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen} > <Modal size="S"> <Dialog> <DialogContent> <DialogHeader> <DialogTitle>Delete Experiment</DialogTitle> <DialogTitleExtra> <DialogCloseButton slot="close" /> </DialogTitleExtra> </DialogHeader> <View padding="size-200"> <Text color="danger"> Are you sure you want to delete this experiment and its annotations and traces? </Text> </View> <View paddingEnd="size-200" paddingTop="size-100" paddingBottom="size-100" borderTopColor="light" borderTopWidth="thin" > <Flex direction="row" justifyContent="end" gap="size-100"> <Button size="S" onPress={() => setIsDeleteDialogOpen(false)}> Cancel </Button> <Button variant="danger" size="S" onPress={() => onDeleteExperiment(props.experimentId)} > Delete Experiment </Button> </Flex> </View> </DialogContent> </Dialog> </Modal> </ModalOverlay> {/* Metadata Dialog */} <ModalOverlay isDismissable isOpen={isMetadataDialogOpen} onOpenChange={setIsMetadataDialogOpen} > <Modal size="S"> <Dialog> <DialogContent> <DialogHeader> <DialogTitle>Metadata</DialogTitle> <DialogTitleExtra> <DialogCloseButton slot="close" /> </DialogTitleExtra> </DialogHeader> <JSONBlock value={JSON.stringify(props.metadata, null, 2)} /> </DialogContent> </Dialog> </Modal> </ModalOverlay> </StopPropagation> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Arize-ai/phoenix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server