From 023b6615a0ef408277d998bbb5629ba50e84c933 Mon Sep 17 00:00:00 2001 From: deep1401 <gandhi0869@gmail.com> Date: Tue, 25 Feb 2025 14:19:54 -0800 Subject: [PATCH] Finally fixed editing of the widget and also moved widget to a separate file --- .../Experiment/DynamicPluginForm.tsx | 250 ++++++++++-------- .../Widgets/CustomEvaluationWidget.tsx | 159 +++++++++++ 2 files changed, 306 insertions(+), 103 deletions(-) create mode 100644 src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx diff --git a/src/renderer/components/Experiment/DynamicPluginForm.tsx b/src/renderer/components/Experiment/DynamicPluginForm.tsx index 5876bd07..8000699d 100644 --- a/src/renderer/components/Experiment/DynamicPluginForm.tsx +++ b/src/renderer/components/Experiment/DynamicPluginForm.tsx @@ -23,11 +23,10 @@ import { Stack, Option, Autocomplete, - Button, - Textarea, } from '@mui/joy'; import { useMemo } from 'react'; import ModelProviderWidget from 'renderer/components/Experiment/Widgets/ModelProviderWidget'; +import CustomEvaluationWidget from './Widgets/CustomEvaluationWidget'; import { RegistryWidgetsType, @@ -462,107 +461,152 @@ function CustomAutocompleteWidget<T = any, S extends StrictRJSFSchema = RJSFSche ); } -type EvaluationField = { - name: string; - expression: string; - return_type: 'boolean' | 'number'; -}; - -const CustomEvaluationWidget = (props: WidgetProps<any>) => { - const { id, value, onChange, disabled, readonly } = props; - // If a value exists and is an array, use it to initialize the state. - const [evalMetrics, setEvalMetrics] = React.useState<EvaluationField[]>(Array.isArray(value) ? value : []); - - React.useEffect(() => { - onChange(evalMetrics); - }, [evalMetrics]); - - const handleAddField = () => { - setEvalMetrics([ - ...evalMetrics, - { name: '', expression: '', return_type: 'boolean' } - ]); - }; - - const handleFieldChange = ( - index: number, - field: keyof EvaluationField, - newValue: string - ) => { - const updated = evalMetrics.map((evaluation, i) => - i === index ? { ...evaluation, [field]: newValue } : evaluation - ); - setEvalMetrics(updated); - }; - - const handleRemoveField = (index: number) => { - const updated = evalMetrics.filter((_, i) => i !== index); - setEvalMetrics(updated); - }; - - return ( - <div id={id}> - {evalMetrics.map((evaluation, index) => ( - <div - key={index} - style={{ - marginBottom: '1rem', - border: '1px solid #ccc', - padding: '0.5rem' - }} - > - <Input - placeholder="Evaluation Name" - value={evaluation.name} - onChange={(e) => - handleFieldChange(index, 'name', e.target.value) - } - disabled={disabled || readonly} - style={{ marginBottom: '0.5rem' }} - /> - <Textarea - placeholder="Regular Expression" - value={evaluation.expression} - onChange={(e) => - handleFieldChange(index, 'expression', e.target.value) - } - disabled={disabled || readonly} - style={{ marginBottom: '0.5rem' }} - /> - <Select - placeholder="Output Type" - value={evaluation.return_type} - onChange={(e, newValue) => - handleFieldChange(index, 'return_type', newValue as string) - } - disabled={disabled || readonly} - style={{ marginBottom: '0.5rem' }} - > - <Option value="boolean">Boolean</Option> - <Option value="number">Number</Option> - </Select> - <Button - onClick={() => handleRemoveField(index)} - disabled={disabled || readonly} - size="sm" - variant="outlined" - > - Remove Field - </Button> - </div> - ))} - <Button - onClick={handleAddField} - disabled={disabled || readonly} - variant="solid" - > - Add Field - </Button> - {/* Hidden input to capture the JSON result on form submission */} - <input type="hidden" id={id} name={id} value={JSON.stringify(evalMetrics)} /> - </div> - ); -}; +// type EvaluationField = { +// name: string; +// expression: string; +// return_type: 'boolean' | 'number'; +// }; + +// const CustomEvaluationWidget = (props: WidgetProps<any>) => { +// const { id, value, onChange, disabled, readonly } = props; +// const valueSent = value; +// console.log("Value", valueSent); + + +// const parseValue = (val: any): EvaluationField[] => { +// if (Array.isArray(val)) { +// if (val.every(item => typeof item === "string")) { +// // If every element is a string: join them and parse the result. +// try { +// const joined = val.join(','); +// console.log("Joined", joined); +// console.log("TYPE", typeof joined); +// const parsed = JSON.parse(joined); +// console.log("PARSED HERE", parsed); +// return Array.isArray(parsed) ? parsed : []; +// } catch (err) { +// console.error("Error parsing evaluation widget value:", err); +// return []; +// } +// } else { +// // If not all elements are strings, assume it's already an array of EvaluationField. +// return val; +// } +// } else if (typeof val === "string") { +// try { +// return JSON.parse(val); +// } catch (err) { +// console.error("Error parsing evaluation widget value string:", err); +// return []; +// } +// } +// return []; +// }; + +// // const parsed = parseValue(value); + +// // Initialize state by parsing the incoming value +// const [evalMetrics, setEvalMetrics] = React.useState<EvaluationField[]>(parseValue(valueSent)); + +// // Update state if a new default value is provided +// React.useEffect(() => { +// const parsed = parseValue(valueSent); +// if (JSON.stringify(parsed) !== JSON.stringify(evalMetrics)) { +// setEvalMetrics(parsed); +// } +// }, [value]); + +// // Propagate state changes upstream. +// React.useEffect(() => { +// onChange(evalMetrics); +// }, [evalMetrics]); + +// const handleAddField = () => { +// setEvalMetrics([ +// ...evalMetrics, +// { name: '', expression: '', return_type: 'boolean' } +// ]); +// }; + +// const handleFieldChange = ( +// index: number, +// field: keyof EvaluationField, +// newValue: string +// ) => { +// const updated = evalMetrics.map((evaluation, i) => +// i === index ? { ...evaluation, [field]: newValue } : evaluation +// ); +// setEvalMetrics(updated); +// }; + +// const handleRemoveField = (index: number) => { +// const updated = evalMetrics.filter((_, i) => i !== index); +// setEvalMetrics(updated); +// }; + +// return ( +// <div id={id}> +// {evalMetrics.map((evaluation, index) => ( +// <div +// key={index} +// style={{ +// marginBottom: '1rem', +// border: '1px solid #ccc', +// padding: '0.5rem' +// }} +// > +// <Input +// placeholder="Evaluation Name" +// value={evaluation.name} +// onChange={(e) => +// handleFieldChange(index, 'name', e.target.value) +// } +// disabled={disabled || readonly} +// style={{ marginBottom: '0.5rem' }} +// /> +// <Textarea +// placeholder="Regular Expression" +// value={evaluation.expression} +// onChange={(e) => +// handleFieldChange(index, 'expression', e.target.value) +// } +// disabled={disabled || readonly} +// style={{ marginBottom: '0.5rem' }} +// /> +// <Select +// placeholder="Output Type" +// value={evaluation.return_type} +// onChange={(e, newValue) => +// handleFieldChange(index, 'return_type', newValue as string) +// } +// disabled={disabled || readonly} +// style={{ marginBottom: '0.5rem' }} +// > +// <Option value="boolean">Boolean</Option> +// <Option value="number">Number</Option> +// </Select> +// <Button +// onClick={() => handleRemoveField(index)} +// disabled={disabled || readonly} +// size="sm" +// variant="outlined" +// > +// Remove Field +// </Button> +// </div> +// ))} +// <Button +// onClick={handleAddField} +// disabled={disabled || readonly} +// variant="solid" +// > +// Add Field +// </Button> +// {/* Hidden input to capture the JSON result on form submission */} +// <input type="hidden" id={id} name={id} value={JSON.stringify(evalMetrics)} /> +// </div> +// ); +// }; function CustomFieldTemplate(props: FieldTemplateProps) { const { diff --git a/src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx b/src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx new file mode 100644 index 00000000..396c57e3 --- /dev/null +++ b/src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx @@ -0,0 +1,159 @@ +import React from 'react'; +import { WidgetProps } from '@rjsf/core'; +import { + Button, + Input, + Select, + Option, + Textarea +} from '@mui/joy'; + +type EvaluationField = { + name: string; + expression: string; + return_type: 'boolean' | 'number'; +}; + + +const CustomEvaluationWidget = (props: WidgetProps<any>) => { + const { id, value, onChange, disabled, readonly } = props; + const valueSent = value; + console.log("Value", valueSent); + + + const parseValue = (val: any): EvaluationField[] => { + if (Array.isArray(val)) { + if (val.every(item => typeof item === "string")) { + // If every element is a string: join them and parse the result. + try { + const joined = val.join(','); + console.log("Joined", joined); + console.log("TYPE", typeof joined); + const parsed = JSON.parse(joined); + console.log("PARSED HERE", parsed); + return Array.isArray(parsed) ? parsed : []; + } catch (err) { + console.error("Error parsing evaluation widget value:", err); + return []; + } + } else { + // If not all elements are strings, assume it's already an array of EvaluationField. + return val; + } + } else if (typeof val === "string") { + try { + return JSON.parse(val); + } catch (err) { + console.error("Error parsing evaluation widget value string:", err); + return []; + } + } + return []; + }; + + // const parsed = parseValue(value); + + // Initialize state by parsing the incoming value + const [evalMetrics, setEvalMetrics] = React.useState<EvaluationField[]>(parseValue(valueSent)); + + // Update state if a new default value is provided + React.useEffect(() => { + const parsed = parseValue(valueSent); + if (JSON.stringify(parsed) !== JSON.stringify(evalMetrics)) { + setEvalMetrics(parsed); + } + }, [value]); + + // Propagate state changes upstream. + React.useEffect(() => { + onChange(evalMetrics); + }, [evalMetrics]); + + const handleAddField = () => { + setEvalMetrics([ + ...evalMetrics, + { name: '', expression: '', return_type: 'boolean' } + ]); + }; + + const handleFieldChange = ( + index: number, + field: keyof EvaluationField, + newValue: string + ) => { + const updated = evalMetrics.map((evaluation, i) => + i === index ? { ...evaluation, [field]: newValue } : evaluation + ); + setEvalMetrics(updated); + }; + + const handleRemoveField = (index: number) => { + const updated = evalMetrics.filter((_, i) => i !== index); + setEvalMetrics(updated); + }; + + return ( + <div id={id}> + {evalMetrics.map((evaluation, index) => ( + <div + key={index} + style={{ + marginBottom: '1rem', + border: '1px solid #ccc', + padding: '0.5rem' + }} + > + <Input + placeholder="Evaluation Name" + value={evaluation.name} + onChange={(e) => + handleFieldChange(index, 'name', e.target.value) + } + disabled={disabled || readonly} + style={{ marginBottom: '0.5rem' }} + /> + <Textarea + placeholder="Regular Expression" + value={evaluation.expression} + onChange={(e) => + handleFieldChange(index, 'expression', e.target.value) + } + disabled={disabled || readonly} + style={{ marginBottom: '0.5rem' }} + /> + <Select + placeholder="Output Type" + value={evaluation.return_type} + onChange={(e, newValue) => + handleFieldChange(index, 'return_type', newValue as string) + } + disabled={disabled || readonly} + style={{ marginBottom: '0.5rem' }} + > + <Option value="boolean">Boolean</Option> + <Option value="number">Number</Option> + </Select> + <Button + onClick={() => handleRemoveField(index)} + disabled={disabled || readonly} + size="sm" + variant="outlined" + > + Remove Field + </Button> + </div> + ))} + <Button + onClick={handleAddField} + disabled={disabled || readonly} + variant="solid" + > + Add Field + </Button> + {/* Hidden input to capture the JSON result on form submission */} + <input type="hidden" id={id} name={id} value={JSON.stringify(evalMetrics)} /> + </div> + ); +}; + +export default CustomEvaluationWidget; -- GitLab