From 4e998f53b0e8c5c0e47c67cb92cde7f89ae12566 Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Wed, 12 Feb 2025 19:31:18 -0800 Subject: [PATCH] Feature: added eval input --- .../sub-block/components/eval-input.tsx | 235 ++++++++++++++++++ .../sub-block/components/tool-input.tsx | 1 - .../components/sub-block/sub-block.tsx | 4 +- blocks/blocks/evaluator.ts | 3 +- blocks/types.ts | 1 + 5 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 app/w/[id]/components/workflow-block/components/sub-block/components/eval-input.tsx diff --git a/app/w/[id]/components/workflow-block/components/sub-block/components/eval-input.tsx b/app/w/[id]/components/workflow-block/components/sub-block/components/eval-input.tsx new file mode 100644 index 0000000000..034cac6d91 --- /dev/null +++ b/app/w/[id]/components/workflow-block/components/sub-block/components/eval-input.tsx @@ -0,0 +1,235 @@ +import { useRef, useState } from 'react' +import { ChevronDown, ChevronUp, Plus, Trash } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { cn } from '@/lib/utils' +import { useSubBlockValue } from '../hooks/use-sub-block-value' + +interface EvalMetric { + id: string + name: string + description: string + range: { + min: number + max: number + } +} + +interface EvalInputProps { + blockId: string + subBlockId: string +} + +export function EvalInput({ blockId, subBlockId }: EvalInputProps) { + const [value, setValue] = useSubBlockValue(blockId, subBlockId) + const containerRef = useRef(null) + + // Initialize with default metrics if value is null + const metrics = value || [ + { + id: crypto.randomUUID(), + name: '', + description: '', + range: { min: 0, max: 1 }, + }, + ] + + const addMetric = () => { + const newMetric: EvalMetric = { + id: crypto.randomUUID(), + name: '', + description: '', + range: { min: 0, max: 1 }, + } + setValue([...metrics, newMetric]) + + // Focus the new metric's name input after a short delay + setTimeout(() => { + const newInput = containerRef.current?.querySelector( + `[data-metric-id="${newMetric.id}"] input[name="name"]` + ) as HTMLInputElement + if (newInput) { + newInput.focus() + } + }, 0) + } + + const removeMetric = (id: string) => { + if (metrics.length === 1) return + setValue(metrics.filter((metric) => metric.id !== id)) + } + + const updateMetric = (id: string, field: keyof EvalMetric, value: any) => { + setValue( + metrics.map((metric) => + metric.id === id + ? { + ...metric, + [field]: value, + } + : metric + ) + ) + } + + const updateRange = (id: string, field: 'min' | 'max', value: string) => { + const numValue = parseFloat(value) + if (isNaN(numValue)) return + + setValue( + metrics.map((metric) => + metric.id === id + ? { + ...metric, + range: { + ...metric.range, + [field]: numValue, + }, + } + : metric + ) + ) + } + + const moveMetric = (id: string, direction: 'up' | 'down') => { + const index = metrics.findIndex((metric) => metric.id === id) + if ( + (direction === 'up' && index === 0) || + (direction === 'down' && index === metrics.length - 1) + ) + return + + const newMetrics = [...metrics] + const targetIndex = direction === 'up' ? index - 1 : index + 1 + ;[newMetrics[index], newMetrics[targetIndex]] = [newMetrics[targetIndex], newMetrics[index]] + setValue(newMetrics) + } + + return ( +
+ {metrics.map((metric, index) => ( +
+
+ Metric {index + 1} +
+ + + + + Add Metric + + +
+ + + + + Move Up + + + + + + + Move Down + +
+ + + + + + Delete Metric + +
+
+ +
+
+ + updateMetric(metric.id, 'name', e.target.value)} + placeholder="Accuracy" + className="text-muted-foreground placeholder:text-muted-foreground/50" + /> +
+ +
+ + updateMetric(metric.id, 'description', e.target.value)} + placeholder="How well does it meet the requirements?" + className="text-muted-foreground placeholder:text-muted-foreground/50" + /> +
+ +
+
+ + updateRange(metric.id, 'min', e.target.value)} + min={0} + max={metric.range.max} + step={0.1} + className="text-muted-foreground placeholder:text-muted-foreground/50" + /> +
+
+ + updateRange(metric.id, 'max', e.target.value)} + min={metric.range.min} + step={0.1} + className="text-muted-foreground placeholder:text-muted-foreground/50" + /> +
+
+
+
+ ))} +
+ ) +} diff --git a/app/w/[id]/components/workflow-block/components/sub-block/components/tool-input.tsx b/app/w/[id]/components/workflow-block/components/sub-block/components/tool-input.tsx index 535d10a2f8..90eed2838b 100644 --- a/app/w/[id]/components/workflow-block/components/sub-block/components/tool-input.tsx +++ b/app/w/[id]/components/workflow-block/components/sub-block/components/tool-input.tsx @@ -22,7 +22,6 @@ interface ToolInputProps { subBlockId: string } -// State interface - only what we need to store interface StoredTool { type: string title: string diff --git a/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx b/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx index e1e2a1b188..91064874bc 100644 --- a/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx +++ b/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx @@ -4,13 +4,13 @@ import { CheckboxList } from './components/checkbox-list' import { Code } from './components/code' import { ConditionInput } from './components/condition-input' import { Dropdown } from './components/dropdown' +import { EvalInput } from './components/eval-input' import { LongInput } from './components/long-input' import { ShortInput } from './components/short-input' import { SliderInput } from './components/slider-input' import { Switch } from './components/switch' import { Table } from './components/table' import { ToolInput } from './components/tool-input' -import { useSubBlockValue } from './hooks/use-sub-block-value' interface SubBlockProps { blockId: string @@ -95,6 +95,8 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) { return ( ) + case 'eval-input': + return default: return null } diff --git a/blocks/blocks/evaluator.ts b/blocks/blocks/evaluator.ts index b657635ba0..cc87be05e3 100644 --- a/blocks/blocks/evaluator.ts +++ b/blocks/blocks/evaluator.ts @@ -185,9 +185,8 @@ export const EvaluatorBlock: BlockConfig = { { id: 'prompt', title: 'Evaluation Criteria', - type: 'long-input', + type: 'eval-input', layout: 'full', - placeholder: 'Evaluate the input based on the following criteria...', }, { id: 'content', diff --git a/blocks/types.ts b/blocks/types.ts index 835196f1d0..0158506dcc 100644 --- a/blocks/types.ts +++ b/blocks/types.ts @@ -20,6 +20,7 @@ export type SubBlockType = | 'tool-input' | 'checkbox-list' | 'condition-input' + | 'eval-input' export type SubBlockLayout = 'full' | 'half' // Tool output type utilities