diff --git a/app/src/pages/datasets/DatasetFromCSVForm.tsx b/app/src/pages/datasets/DatasetFromCSVForm.tsx
index 8c15bf8c64..bb4b4f6566 100644
--- a/app/src/pages/datasets/DatasetFromCSVForm.tsx
+++ b/app/src/pages/datasets/DatasetFromCSVForm.tsx
@@ -131,7 +131,7 @@ export function DatasetFromCSVForm(props: CreateDatasetFromCSVFormProps) {
value={value.toString()}
>
-
+
{error?.message ? (
{error.message}
) : (
@@ -154,7 +154,7 @@ export function DatasetFromCSVForm(props: CreateDatasetFromCSVFormProps) {
value={value.toString()}
>
-
+
{error?.message ? (
{error.message}
) : (
diff --git a/app/src/pages/datasets/DatasetFromJSONForm.tsx b/app/src/pages/datasets/DatasetFromJSONForm.tsx
new file mode 100644
index 0000000000..9421db70b5
--- /dev/null
+++ b/app/src/pages/datasets/DatasetFromJSONForm.tsx
@@ -0,0 +1,250 @@
+import React, { useCallback, useState } from "react";
+import { Controller, useForm } from "react-hook-form";
+import { css } from "@emotion/react";
+
+import {
+ Button,
+ FieldError,
+ Flex,
+ Form,
+ Input,
+ Label,
+ Text,
+ TextArea,
+ TextField,
+ View,
+} from "@phoenix/components";
+import { JSONBlock } from "@phoenix/components/code";
+import { prependBasename } from "@phoenix/utils/routingUtils";
+
+type CreateDatasetFromJSONParams = {
+ file: FileList;
+ name: string;
+ description: string;
+};
+
+type JsonData = {
+ name: string;
+ description: string;
+ inputs: Record[];
+ outputs: Record[];
+ metadata: Record[];
+};
+
+export type CreateDatasetFromJSONFormProps = {
+ onDatasetCreated: (dataset: { id: string; name: string }) => void;
+ onDatasetCreateError: (error: Error) => void;
+};
+
+export function DatasetFromJSONForm(props: CreateDatasetFromJSONFormProps) {
+ const { onDatasetCreated, onDatasetCreateError } = props;
+ const [jsonData, setJsonData] = useState(null);
+ const {
+ control,
+ handleSubmit,
+ formState: { isDirty, isValid },
+ } = useForm({
+ defaultValues: {
+ name: "Dataset " + new Date().toISOString(),
+ description: "",
+ file: undefined,
+ },
+ });
+
+ const onSubmit = useCallback(
+ (data: CreateDatasetFromJSONParams) => {
+ if (!jsonData) {
+ onDatasetCreateError(new Error("No JSON file has been uploaded"));
+ return;
+ }
+
+ return fetch(prependBasename("/v1/datasets/upload?sync=true"), {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ ...jsonData,
+ name: data.name,
+ description: data.description,
+ }),
+ })
+ .then((response) => {
+ if (!response.ok) {
+ return response.text().then((errorText) => {
+ throw onDatasetCreateError(
+ new Error(errorText || "Failed to create dataset")
+ );
+ });
+ }
+ return response.json();
+ })
+ .then((res) => {
+ onDatasetCreated({
+ name: data.name,
+ id: res["data"]["dataset_id"],
+ });
+ });
+ },
+ [onDatasetCreateError, onDatasetCreated, jsonData]
+ );
+
+ return (
+
+ );
+}
diff --git a/app/src/pages/datasets/DatasetsPage.tsx b/app/src/pages/datasets/DatasetsPage.tsx
index feffa85f5d..5fafc9b1fa 100644
--- a/app/src/pages/datasets/DatasetsPage.tsx
+++ b/app/src/pages/datasets/DatasetsPage.tsx
@@ -11,6 +11,7 @@ import { getErrorMessagesFromRelayMutationError } from "@phoenix/utils/errorUtil
import { DatasetsPageQuery } from "./__generated__/DatasetsPageQuery.graphql";
import { DatasetFromCSVForm } from "./DatasetFromCSVForm";
+import { DatasetFromJSONForm } from "./DatasetFromJSONForm";
import { DatasetsTable } from "./DatasetsTable";
export function DatasetsPage() {
@@ -64,6 +65,7 @@ type CreateDatasetActionMenu = {
enum CreateDatasetAction {
NEW = "newDataset",
FROM_CSV = "datasetFromCSV",
+ FROM_JSON = "datasetFromJSON",
}
function CreateDatasetActionMenu({
@@ -133,6 +135,36 @@ function CreateDatasetActionMenu({
);
};
+ const onCreateDatasetFromJSON = () => {
+ setDialog(
+
+ );
+ };
return (
<>
- New Dataset
- Dataset from CSV
+ - Dataset from JSON