Skip to main content
Glama

@arizeai/phoenix-mcp

Official
by Arize-ai
DatasetFromCSVForm.tsx10.3 kB
import { useCallback, useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { css } from "@emotion/react"; import { Field, FieldProps } from "@arizeai/components"; import { Button, FieldError, Flex, Form, Input, Label, ListBox, ListBoxItem, Popover, Select, SelectChevronUpDownIcon, SelectValue, Text, TextArea, TextField, View, } from "@phoenix/components"; import { fieldBaseCSS } from "@phoenix/components/field/styles"; import { prependBasename } from "@phoenix/utils/routingUtils"; type CreateDatasetFromCSVParams = { file: FileList; input_keys: string[]; output_keys: string[]; metadata_keys: string[]; name: string; description: string; metadata: Record<string, unknown>; }; export type CreateDatasetFromCSVFormProps = { onDatasetCreated: (dataset: { id: string; name: string }) => void; onDatasetCreateError: (error: Error) => void; }; function getColumnNames(csvText: string) { const lines = csvText.split("\n"); if (lines.length > 0) { return lines[0].split(",").map((name) => name.trim()); } return []; } export function DatasetFromCSVForm(props: CreateDatasetFromCSVFormProps) { const { onDatasetCreated, onDatasetCreateError } = props; const [columns, setColumns] = useState<string[]>([]); const { control, handleSubmit, resetField, setValue, formState: { isDirty, isValid }, } = useForm<CreateDatasetFromCSVParams>({ defaultValues: { name: "", input_keys: [], output_keys: [], metadata_keys: [], description: "", metadata: {}, }, }); const onSubmit = useCallback( (data: CreateDatasetFromCSVParams) => { const formData = new FormData(); formData.append("file", data.file[0]); formData.append("name", data.name); formData.append("description", data.description); formData.append("metadata", JSON.stringify(data.metadata)); data.input_keys.forEach((key) => { formData.append("input_keys[]", key); }); data.output_keys.forEach((key) => { formData.append("output_keys[]", key); }); data.metadata_keys.forEach((key) => { formData.append("metadata_keys[]", key); }); return fetch(prependBasename("/v1/datasets/upload?sync=true"), { method: "POST", body: formData, }) .then((response) => { if (!response.ok) { throw onDatasetCreateError( new Error(response.statusText || "Failed to create dataset") ); } return response.json(); }) .then((res) => { onDatasetCreated({ name: data.name, id: res["data"]["dataset_id"], }); }); }, [onDatasetCreateError, onDatasetCreated] ); return ( <Form onSubmit={handleSubmit(onSubmit)}> <div css={css` padding: var(--ac-global-dimension-size-200); .ac-dropdown-button { width: 100%; } `} > <Controller control={control} name="file" rules={{ required: "CSV file is required" }} render={({ field: { value: _value, onChange, ...field } }) => { return ( <div css={css( fieldBaseCSS, css` display: flex; flex-direction: column; gap: var(--ac-global-dimension-size-50); margin-bottom: var(--ac-global-dimension-size-200); ` )} > <Label>CSV file</Label> <input {...field} onChange={(event) => { onChange(event.target.files); // Reset columns when a new file is uploaded resetField("input_keys"); resetField("output_keys"); resetField("metadata_keys"); const file = event.target.files?.[0]; if (file) { const name = file.name.split(".")[0]; const reader = new FileReader(); reader.onload = function (e) { if (!e.target) { return; } const text = e.target.result; const columnNames = getColumnNames(text as string); setColumns(columnNames); setValue("name", name); }; reader.readAsText(file); } }} type="file" id="file" accept=".csv" /> </div> ); }} /> <Controller name="name" control={control} rules={{ required: "field is required", }} render={({ field: { onChange, onBlur, value }, fieldState: { invalid, error }, }) => ( <TextField isInvalid={invalid} onChange={onChange} onBlur={onBlur} value={value.toString()} > <Label>Dataset Name</Label> <Input placeholder="e.x. Golden Dataset" /> {error?.message ? ( <FieldError>{error.message}</FieldError> ) : ( <Text slot="description">The name of the dataset</Text> )} </TextField> )} /> <Controller name="description" control={control} render={({ field: { onChange, onBlur, value }, fieldState: { invalid, error }, }) => ( <TextField isInvalid={invalid} onChange={onChange} onBlur={onBlur} value={value.toString()} > <Label>Description</Label> <TextArea placeholder="e.x. A dataset for structured data extraction" /> {error?.message ? ( <FieldError>{error.message}</FieldError> ) : ( <Text slot="description">The description of the dataset</Text> )} </TextField> )} /> <Controller name="input_keys" control={control} rules={{ required: "field is required", }} render={({ field: { value, onChange }, fieldState: { invalid, error }, }) => ( <ColumnMultiSelector label="input keys" validationState={invalid ? "invalid" : "valid"} description={`the columns to use as input`} columns={columns} selectedColumns={value} onChange={onChange} errorMessage={error?.message} /> )} /> <Controller name="output_keys" control={control} render={({ field: { value, onChange }, fieldState: { invalid, error }, }) => ( <ColumnMultiSelector label="output keys" validationState={invalid ? "invalid" : "valid"} description={`the columns to use as output`} columns={columns} selectedColumns={value} onChange={onChange} errorMessage={error?.message} /> )} /> <Controller name="metadata_keys" control={control} render={({ field: { value, onChange }, fieldState: { invalid, error }, }) => ( <ColumnMultiSelector label="metadata keys" validationState={invalid ? "invalid" : "valid"} description={`the columns to use as metadata`} columns={columns} selectedColumns={value} onChange={onChange} errorMessage={error?.message} /> )} /> </div> <View paddingEnd="size-200" paddingTop="size-100" paddingBottom="size-100" borderTopColor="light" borderTopWidth="thin" > <Flex direction="row" justifyContent="end"> <Button type="submit" isDisabled={!isValid} variant={isDirty ? "primary" : "default"} size="S" > Create Dataset </Button> </Flex> </View> </Form> ); } function ColumnMultiSelector( props: Pick< FieldProps, "label" | "validationState" | "description" | "errorMessage" > & { label: string; columns: string[]; selectedColumns: string[]; onChange: (selectedColumns: string[]) => void; } ) { const { columns, selectedColumns, onChange, label, validationState, description, errorMessage, } = props; const noColumns = columns.length === 0; const items = useMemo(() => { return columns.map((column) => ({ id: column, value: column })); }, [columns]); return ( <Field label={label} isDisabled={noColumns} validationState={validationState} description={description} errorMessage={errorMessage} > <Select isDisabled={noColumns} placeholder="Select columns" selectionMode="multiple" onChange={(keys) => { if (keys === "all") { return onChange(columns); } return onChange(Array.from(keys as string[])); }} value={selectedColumns} > <Button> <SelectValue /> <SelectChevronUpDownIcon /> </Button> <Popover> <ListBox renderEmptyState={() => "No columns to select"} items={items} > {(item) => <ListBoxItem id={item.id}>{item.value}</ListBoxItem>} </ListBox> </Popover> </Select> </Field> ); }

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