From 31ba221c109622d1c9f062d0f1a17540b129ebba Mon Sep 17 00:00:00 2001 From: Anurag Singh Date: Sun, 21 Dec 2025 03:35:16 +0530 Subject: [PATCH 1/5] feat(workflow): added structured output capabilities in the AI SDK agent, allowing generation from JSON examples and schemas. Signed-off-by: Anurag Singh --- .gitignore | 1 + .../src/components/workflow/ConfigPanel.tsx | 26 +- .../components/workflow/ParameterField.tsx | 41 +- .../src/components/workflow/WorkflowNode.tsx | 3 +- frontend/src/schemas/component.ts | 2 + packages/component-sdk/src/types.ts | 2 + .../components/ai/__tests__/ai-agent.test.ts | 260 +++++++++++- worker/src/components/ai/ai-agent.ts | 376 ++++++++++++++++-- worker/tsconfig.tsbuildinfo | 2 +- 9 files changed, 650 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 4976639c..39baff37 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ lerna-debug.log* !.vscode/extensions.json !.vscode/settings.json .claude/ +CLAUDE.md .idea/ .DS_Store *.suo diff --git a/frontend/src/components/workflow/ConfigPanel.tsx b/frontend/src/components/workflow/ConfigPanel.tsx index e2a02248..13cbd2d1 100644 --- a/frontend/src/components/workflow/ConfigPanel.tsx +++ b/frontend/src/components/workflow/ConfigPanel.tsx @@ -868,22 +868,19 @@ export function ConfigPanel({ defaultOpen={true} >
- {/* Sort parameters: select types first, then others */} - {componentParameters - .slice() - .sort((a, b) => { - // Select parameters go first - const aIsSelect = a.type === 'select' - const bIsSelect = b.type === 'select' - if (aIsSelect && !bIsSelect) return -1 - if (!aIsSelect && bIsSelect) return 1 - return 0 - }) - .map((param, index) => ( + {/* Render parameters in component definition order to preserve hierarchy */} + {componentParameters.map((param, index) => { + // Only show border between top-level parameters (not nested ones) + const isTopLevel = !param.visibleWhen + const prevParam = index > 0 ? componentParameters[index - 1] : null + const prevIsTopLevel = prevParam ? !prevParam.visibleWhen : false + const showBorder = index > 0 && isTopLevel && prevIsTopLevel + + return (
0 && "border-t border-border pt-3" + showBorder && "border-t border-border pt-3" )} >
- ))} + ) + })}
)} diff --git a/frontend/src/components/workflow/ParameterField.tsx b/frontend/src/components/workflow/ParameterField.tsx index 95f27f67..ea43153b 100644 --- a/frontend/src/components/workflow/ParameterField.tsx +++ b/frontend/src/components/workflow/ParameterField.tsx @@ -1008,6 +1008,35 @@ interface ParameterFieldWrapperProps { onUpdateParameter?: (paramId: string, value: any) => void } +/** + * Checks if a parameter should be visible based on its visibleWhen conditions. + * Returns true if all conditions are met or if no conditions exist. + */ +function shouldShowParameter( + parameter: Parameter, + allParameters: Record | undefined +): boolean { + // If no visibleWhen conditions, always show + if (!parameter.visibleWhen) { + return true + } + + // If we have conditions but no parameter values to check against, hide by default + if (!allParameters) { + return false + } + + // Check all conditions in visibleWhen object + for (const [key, expectedValue] of Object.entries(parameter.visibleWhen)) { + const actualValue = allParameters[key] + if (actualValue !== expectedValue) { + return false + } + } + + return true +} + /** * ParameterFieldWrapper - Wraps parameter field with label and description */ @@ -1020,6 +1049,11 @@ export function ParameterFieldWrapper({ parameters, onUpdateParameter, }: ParameterFieldWrapperProps) { + // Check visibility conditions + if (!shouldShowParameter(parameter, parameters)) { + return null + } + // Special case: Runtime Inputs Editor for Entry Point if (parameter.id === 'runtimeInputs') { return ( @@ -1041,11 +1075,14 @@ export function ParameterFieldWrapper({ ) } + // Check if this is a nested/conditional parameter (has visibleWhen) + const isNestedParameter = Boolean(parameter.visibleWhen) + // Standard parameter field rendering return ( -
+
-
) diff --git a/frontend/src/components/workflow/ParameterField.tsx b/frontend/src/components/workflow/ParameterField.tsx index ea43153b..43749dbc 100644 --- a/frontend/src/components/workflow/ParameterField.tsx +++ b/frontend/src/components/workflow/ParameterField.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Checkbox } from '@/components/ui/checkbox' +import { Switch } from '@/components/ui/switch' import { Button } from '@/components/ui/button' import { useNavigate } from 'react-router-dom' import { RuntimeInputsEditor } from './RuntimeInputsEditor' @@ -724,9 +725,9 @@ export function ParameterField({ } try { - const parsed = JSON.parse(nextValue) + JSON.parse(nextValue) // Validate JSON syntax setJsonError(null) - onChange(parsed) + onChange(nextValue) // Pass string, not parsed object - backend expects string } catch (error) { setJsonError('Invalid JSON') // Keep showing error, don't update parent @@ -1006,6 +1007,7 @@ interface ParameterFieldWrapperProps { componentId?: string parameters?: Record | undefined onUpdateParameter?: (paramId: string, value: any) => void + allComponentParameters?: Parameter[] } /** @@ -1037,6 +1039,22 @@ function shouldShowParameter( return true } +/** + * Checks if a boolean parameter acts as a header toggle (controls visibility of other params). + * Returns true if other parameters have visibleWhen conditions referencing this parameter. + */ +function isHeaderToggleParameter( + parameter: Parameter, + allComponentParameters: Parameter[] | undefined +): boolean { + if (parameter.type !== 'boolean' || !allComponentParameters) return false + + // Check if any other parameter has visibleWhen referencing this param + return allComponentParameters.some( + (p) => p.visibleWhen && parameter.id in p.visibleWhen + ) +} + /** * ParameterFieldWrapper - Wraps parameter field with label and description */ @@ -1048,6 +1066,7 @@ export function ParameterFieldWrapper({ componentId, parameters, onUpdateParameter, + allComponentParameters, }: ParameterFieldWrapperProps) { // Check visibility conditions if (!shouldShowParameter(parameter, parameters)) { @@ -1078,9 +1097,35 @@ export function ParameterFieldWrapper({ // Check if this is a nested/conditional parameter (has visibleWhen) const isNestedParameter = Boolean(parameter.visibleWhen) + // Check if this is a header toggle (boolean that controls other params' visibility) + const isHeaderToggle = isHeaderToggleParameter(parameter, allComponentParameters) + + // Header toggle rendering - label left, switch right + if (isHeaderToggle) { + return ( +
+
+ + onChange(checked)} + /> +
+ {parameter.description && ( +

+ {parameter.description} +

+ )} +
+ ) + } + // Standard parameter field rendering return ( -
+