From 95c7c3ce5e1e6e9b40bc1332816293b69aa4480a Mon Sep 17 00:00:00 2001 From: Ryan Holinshead <> Date: Sat, 6 Jan 2024 15:33:18 -0500 Subject: [PATCH] [editor] ErrorBoundary Renderer for PromptInput # [editor] ErrorBoundary Renderer for PromptInput With the ability to set arbitrary JSON for the prompt input, if we have a PromptSchema which states what the input should look like, we should be able to do some basic validation that the input at least matches the general type (for now, compare 'string' PromptInput vs object -- in the future, we could do some basic validation against the schema). If there is any error with rendering the schema-based rendering for the input, we can use ErrorBoundary to fallback to an error message informing the user that the format is invalid and to toggle to JSON editor to fix. Screenshot 2024-01-05 at 11 01 07 PM Toggling to the JSON editor will clear the error state and try to render the correct Schema renderer when toggling back. Note that in dev mode, these errors are still propagated to a top-level error screen which you can dismiss to show the fallback. In prod, that won't happen and will just show the fallback and an error in the console. We will do the same for the SettingsRenderer in a subsequent PR ## Testing: - Updated `OpenAIChatModelParserPromptSchema` input to match that of `OpenAIChatVisionModelParserPromptSchema` while the input is still string. See the error fallback - Toggle to JSON editor and back, ensure error fallback still shows - Toggle to JSON editor and update to valid input ``` { "attachments": [ { "data": "https://s3.amazonaws.com/files.uploads.lastmileai.com/uploads/cldxsqbel0000qs8owp8mkd0z/2023_12_1_21_23_24/942/Screenshot 2023-11-28 at 11.11.25 AM.png", "mime_type": "image/png" }, { "data": "https://s3.amazonaws.com/files.uploads.lastmileai.com/uploads/cldxsqbel0000qs8owp8mkd0z/2023_12_1_21_23_24/8325/Screenshot 2023-11-28 at 1.51.52 PM.png", "mime_type": "image/png" } ], "data": "What do these images show?" } ``` and see proper schema rendering https://github.com/lastmile-ai/aiconfig/assets/5060851/1343a201-e5eb-46a0-a7f5-a4bdb228d120 --- .../src/aiconfig/editor/client/package.json | 1 + .../src/components/JSONEditorToggleButton.tsx | 20 +++++ .../prompt_input/PromptInputRenderer.tsx | 87 +++++++++++++++---- .../PromptInputSchemaRenderer.tsx | 3 +- python/src/aiconfig/editor/client/yarn.lock | 7 ++ 5 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 python/src/aiconfig/editor/client/src/components/JSONEditorToggleButton.tsx diff --git a/python/src/aiconfig/editor/client/package.json b/python/src/aiconfig/editor/client/package.json index 5b360f21f..55cf56403 100644 --- a/python/src/aiconfig/editor/client/package.json +++ b/python/src/aiconfig/editor/client/package.json @@ -41,6 +41,7 @@ "oboe": "^2.1.5", "react": "^18", "react-dom": "^18", + "react-error-boundary": "^4.0.12", "react-markdown": "^8.0.6", "react-scripts": "5.0.1", "remark-gfm": "^4.0.0", diff --git a/python/src/aiconfig/editor/client/src/components/JSONEditorToggleButton.tsx b/python/src/aiconfig/editor/client/src/components/JSONEditorToggleButton.tsx new file mode 100644 index 000000000..4da61d451 --- /dev/null +++ b/python/src/aiconfig/editor/client/src/components/JSONEditorToggleButton.tsx @@ -0,0 +1,20 @@ +import { ActionIcon, Tooltip } from "@mantine/core"; +import { IconBraces, IconBracesOff } from "@tabler/icons-react"; + +type Props = { + isRawJSON: boolean; + setIsRawJSON: (value: boolean) => void; +}; + +export default function JSONEditorToggleButton({ + isRawJSON, + setIsRawJSON, +}: Props) { + return ( + + setIsRawJSON(!isRawJSON)}> + {isRawJSON ? : } + + + ); +} diff --git a/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/PromptInputRenderer.tsx b/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/PromptInputRenderer.tsx index f05e6aafd..bd8f97431 100644 --- a/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/PromptInputRenderer.tsx +++ b/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/PromptInputRenderer.tsx @@ -3,9 +3,12 @@ import { memo, useState } from "react"; import { PromptInputSchema } from "../../../utils/promptUtils"; import PromptInputSchemaRenderer from "./schema_renderer/PromptInputSchemaRenderer"; import PromptInputConfigRenderer from "./PromptInputConfigRenderer"; -import { ActionIcon, Flex, Tooltip } from "@mantine/core"; -import { IconBraces, IconBracesOff } from "@tabler/icons-react"; +import { Flex } from "@mantine/core"; import PromptInputJSONRenderer from "./PromptInputJSONRenderer"; +import { ErrorBoundary, useErrorBoundary } from "react-error-boundary"; +import { Text } from "@mantine/core"; +import JSONRenderer from "../../JSONRenderer"; +import JSONEditorToggleButton from "../../JSONEditorToggleButton"; type Props = { input: PromptInput; @@ -13,17 +16,50 @@ type Props = { onChangeInput: (value: PromptInput) => void; }; +type ErrorFallbackProps = { + input: PromptInput; + toggleJSONEditor: () => void; +}; + +function InputErrorFallback({ input, toggleJSONEditor }: ErrorFallbackProps) { + const { resetBoundary: clearRenderError } = useErrorBoundary(); + return ( + + + Invalid input format for model. Toggle JSON editor to update + + + + { + clearRenderError(); + toggleJSONEditor(); + }} + /> + + + ); +} + export default memo(function PromptInputRenderer({ input, schema, onChangeInput, }: Props) { const [isRawJSON, setIsRawJSON] = useState(false); - return ( + const rawJSONToggleButton = ( + + + + ); + + const nonJSONRenderer = ( <> - {isRawJSON ? ( - - ) : schema ? ( + {schema ? ( )} - - - setIsRawJSON((curr) => !curr)}> - {isRawJSON ? ( - - ) : ( - - )} - - - + {rawJSONToggleButton} + + ); + + return ( + <> + {isRawJSON ? ( + <> + + {rawJSONToggleButton} + + ) : ( + ( + setIsRawJSON(true)} + /> + )} + > + {nonJSONRenderer} + + )} ); }); diff --git a/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/schema_renderer/PromptInputSchemaRenderer.tsx b/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/schema_renderer/PromptInputSchemaRenderer.tsx index 08fcda5ff..ca1b32ef6 100644 --- a/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/schema_renderer/PromptInputSchemaRenderer.tsx +++ b/python/src/aiconfig/editor/client/src/components/prompt/prompt_input/schema_renderer/PromptInputSchemaRenderer.tsx @@ -27,8 +27,7 @@ function SchemaRenderer({ input, schema, onChangeInput }: SchemaRendererProps) { } = schema.properties; if (typeof input === "string") { - return null; - // TODO: Add ErrorBoundary handling and throw error here + throw new Error("Expected input type object but got string"); } const { data, attachments, ..._restData } = input; diff --git a/python/src/aiconfig/editor/client/yarn.lock b/python/src/aiconfig/editor/client/yarn.lock index f766e5e80..57d10c5f1 100644 --- a/python/src/aiconfig/editor/client/yarn.lock +++ b/python/src/aiconfig/editor/client/yarn.lock @@ -9594,6 +9594,13 @@ react-dropzone@14.2.3: file-selector "^0.6.0" prop-types "^15.8.1" +react-error-boundary@^4.0.12: + version "4.0.12" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.12.tgz#59f8f1dbc53bbbb34fc384c8db7cf4082cb63e2c" + integrity sha512-kJdxdEYlb7CPC1A0SeUY38cHpjuu6UkvzKiAmqmOFL21VRfMhOcWxTCBgLVCO0VEMh9JhFNcVaXlV4/BTpiwOA== + dependencies: + "@babel/runtime" "^7.12.5" + react-error-overlay@^6.0.11: version "6.0.11" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"