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 90eed2838b..44e51f4c2d 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 @@ -39,7 +39,7 @@ interface ToolParam { // Assumes the first tool in the access array is the tool to be used // TODO: Switch to getting tools instead of tool blocks once we switch to providers const getToolIdFromBlock = (blockType: string): string | undefined => { - const block = getAllBlocks().find((block) => block.type === blockType) + const block = getAllBlocks().find((block) => block.id === blockType) return block?.tools.access[0] } @@ -62,7 +62,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) { const [open, setOpen] = useState(false) const isWide = useWorkflowStore((state) => state.blocks[blockId]?.isWide) - const toolBlocks = getAllBlocks().filter((block) => block.toolbar.category === 'tools') + const toolBlocks = getAllBlocks().filter((block) => block.category === 'tools') const selectedTools: StoredTool[] = Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' @@ -71,15 +71,15 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) { const handleSelectTool = (toolBlock: (typeof toolBlocks)[0]) => { // Check if tool already exists - if (selectedTools.some((tool) => tool.type === toolBlock.type)) { + if (selectedTools.some((tool) => tool.type === toolBlock.id)) { setOpen(false) return } - const toolId = getToolIdFromBlock(toolBlock.type) + const toolId = getToolIdFromBlock(toolBlock.id) const newTool: StoredTool = { - type: toolBlock.type, - title: toolBlock.toolbar.title, + type: toolBlock.id, + title: toolBlock.name, params: {}, isExpanded: true, } @@ -135,12 +135,6 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) { return } - // Helper function to get the icon component for a tool type - const getToolIcon = (type: string) => { - const toolBlock = toolBlocks.find((block) => block.type === type) - return toolBlock?.toolbar.icon - } - return (
{selectedTools.length === 0 ? ( @@ -161,17 +155,17 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) { {toolBlocks.map((block) => ( handleSelectTool(block)} className="flex items-center gap-2 cursor-pointer" >
- +
- {block.toolbar.title} + {block.name}
))}
@@ -182,7 +176,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) { ) : (
{selectedTools.map((tool) => { - const toolBlock = toolBlocks.find((block) => block.type === tool.type) + const toolBlock = toolBlocks.find((block) => block.id === tool.type) const toolId = getToolIdFromBlock(tool.type) const requiredParams = toolId ? getRequiredToolParams(toolId) : [] @@ -199,12 +193,9 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
- +
{tool.title}
@@ -278,17 +269,17 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) { {toolBlocks.map((block) => ( handleSelectTool(block)} className="flex items-center gap-2 cursor-pointer" >
- +
- {block.toolbar.title} + {block.name}
))}
diff --git a/app/w/[id]/components/workflow-block/workflow-block.tsx b/app/w/[id]/components/workflow-block/workflow-block.tsx index bc1228033f..1098147857 100644 --- a/app/w/[id]/components/workflow-block/workflow-block.tsx +++ b/app/w/[id]/components/workflow-block/workflow-block.tsx @@ -21,7 +21,6 @@ interface WorkflowBlockProps { // Combine both interfaces into a single component export function WorkflowBlock({ id, data, selected }: NodeProps) { const { type, config, name } = data - const { toolbar, workflow } = config // State management const [isConnecting, setIsConnecting] = useState(false) @@ -146,7 +145,7 @@ export function WorkflowBlock({ id, data, selected }: NodeProps { @@ -209,9 +208,9 @@ export function WorkflowBlock({ id, data, selected }: NodeProps
- +
{isEditing ? ( b.type === data.type).length + 1 + const name = `${blockConfig.name} ${ + Object.values(blocks).filter((b) => b.id === data.type).length + 1 }` addBlock(id, data.type, name, position) diff --git a/app/w/components/console/components/console-entry/console-entry.tsx b/app/w/components/console/components/console-entry/console-entry.tsx index e129875a4d..2a8989c659 100644 --- a/app/w/components/console/components/console-entry/console-entry.tsx +++ b/app/w/components/console/components/console-entry/console-entry.tsx @@ -26,7 +26,7 @@ export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) { return getBlock(entry.blockType) }, [entry.blockType]) - const BlockIcon = blockConfig?.toolbar.icon + const BlockIcon = blockConfig?.icon const statusIcon = entry.error ? ( diff --git a/app/w/components/console/components/json-view/json-view.tsx b/app/w/components/console/components/json-view/json-view.tsx index 2cc05c46fe..98891dd556 100644 --- a/app/w/components/console/components/json-view/json-view.tsx +++ b/app/w/components/console/components/json-view/json-view.tsx @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react' -import { ChevronDown, ChevronRight } from 'lucide-react' import { Button } from '@/components/ui/button' interface JSONViewProps { diff --git a/app/w/components/toolbar/components/toolbar-block/toolbar-block.tsx b/app/w/components/toolbar/components/toolbar-block/toolbar-block.tsx index e35725c0aa..66e8c9c9fe 100644 --- a/app/w/components/toolbar/components/toolbar-block/toolbar-block.tsx +++ b/app/w/components/toolbar/components/toolbar-block/toolbar-block.tsx @@ -6,7 +6,7 @@ export type ToolbarBlockProps = { export function ToolbarBlock({ config }: ToolbarBlockProps) { const handleDragStart = (e: React.DragEvent) => { - e.dataTransfer.setData('application/json', JSON.stringify({ type: config.type })) + e.dataTransfer.setData('application/json', JSON.stringify({ type: config.id })) e.dataTransfer.effectAllowed = 'move' } @@ -18,17 +18,17 @@ export function ToolbarBlock({ config }: ToolbarBlockProps) { >
-
-

{config.toolbar.title}

-

{config.toolbar.description}

+

{config.name}

+

{config.description}

) diff --git a/app/w/components/toolbar/toolbar.tsx b/app/w/components/toolbar/toolbar.tsx index 57048f7fec..af2b618ff4 100644 --- a/app/w/components/toolbar/toolbar.tsx +++ b/app/w/components/toolbar/toolbar.tsx @@ -23,8 +23,7 @@ export function Toolbar() { const query = searchQuery.toLowerCase() return getAllBlocks().filter( (block) => - block.toolbar.title.toLowerCase().includes(query) || - block.toolbar.description.toLowerCase().includes(query) + block.name.toLowerCase().includes(query) || block.description.toLowerCase().includes(query) ) }, [searchQuery, activeTab]) @@ -70,7 +69,7 @@ export function Toolbar() {
{blocks.map((block) => ( - + ))}
diff --git a/app/w/hooks/use-workflow-execution.ts b/app/w/hooks/use-workflow-execution.ts index 632c4313a8..2a14cea56c 100644 --- a/app/w/hooks/use-workflow-execution.ts +++ b/app/w/hooks/use-workflow-execution.ts @@ -67,7 +67,7 @@ export function useWorkflowExecution() { endedAt: log.endedAt, workflowId: activeWorkflowId, timestamp: log.startedAt, - blockName: log.blockTitle, + blockName: log.blockName, blockType: log.blockType, }) }) @@ -76,7 +76,7 @@ export function useWorkflowExecution() { if (result.logs) { console.group('Detailed Block Logs') result.logs.forEach((log) => { - console.log(`Block ${log.blockTitle}: Success=${log.success}`, { + console.log(`Block ${log.blockName}: Success=${log.success}`, { output: log.output, error: log.error, durationMs: log.durationMs, diff --git a/blocks/blocks/agent.ts b/blocks/blocks/agent.ts index 85bf15bc35..2e220393bf 100644 --- a/blocks/blocks/agent.ts +++ b/blocks/blocks/agent.ts @@ -23,14 +23,64 @@ interface AgentResponse extends ToolResponse { } export const AgentBlock: BlockConfig = { - type: 'agent', - toolbar: { - title: 'Agent', - description: 'Build an agent', - bgColor: '#7F2FFF', - icon: AgentIcon, - category: 'blocks', - }, + id: 'agent', + name: 'Agent', + description: 'Build an agent', + category: 'blocks', + bgColor: '#7F2FFF', + icon: AgentIcon, + subBlocks: [ + { + id: 'systemPrompt', + title: 'System Prompt', + type: 'long-input', + layout: 'full', + placeholder: 'Enter system prompt...', + }, + { + id: 'context', + title: 'User Prompt', + type: 'short-input', + layout: 'full', + placeholder: 'Enter context or user message...', + }, + { + id: 'model', + title: 'Model', + type: 'dropdown', + layout: 'half', + options: Object.keys(MODEL_TOOLS), + }, + { + id: 'temperature', + title: 'Temperature', + type: 'slider', + layout: 'half', + min: 0, + max: 2, + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your API key', + password: true, + connectionDroppable: false, + }, + { + id: 'tools', + title: 'Tools', + type: 'tool-input', + layout: 'full', + }, + { + id: 'responseFormat', + title: 'Response Format', + type: 'code', + layout: 'full', + }, + ], tools: { access: [ 'openai_chat', @@ -54,165 +104,97 @@ export const AgentBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - systemPrompt: { type: 'string', required: true }, - context: { type: 'string', required: false }, - model: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - responseFormat: { - type: 'json', - required: false, - description: - 'Define the expected response format. If not provided, returns plain text content.', - schema: { - type: 'object', - properties: { - fields: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - description: 'Name of the field', - }, - type: { - type: 'string', - enum: ['string', 'number', 'boolean', 'array', 'object'], - description: 'Type of the field', - }, - isArray: { - type: 'boolean', - description: 'Whether this field contains multiple values', - }, - items: { - type: 'object', - description: 'Schema for array items (required when isArray is true)', + inputs: { + systemPrompt: { type: 'string', required: true }, + context: { type: 'string', required: false }, + model: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + responseFormat: { + type: 'json', + required: false, + description: + 'Define the expected response format. If not provided, returns plain text content.', + schema: { + type: 'object', + properties: { + fields: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Name of the field', + }, + type: { + type: 'string', + enum: ['string', 'number', 'boolean', 'array', 'object'], + description: 'Type of the field', + }, + isArray: { + type: 'boolean', + description: 'Whether this field contains multiple values', + }, + items: { + type: 'object', + description: 'Schema for array items (required when isArray is true)', + properties: { + type: { + type: 'string', + enum: ['string', 'number', 'boolean', 'object'], + }, properties: { - type: { - type: 'string', - enum: ['string', 'number', 'boolean', 'object'], - }, - properties: { - type: 'array', - items: { - type: 'object', - properties: { - name: { type: 'string' }, - type: { - type: 'string', - enum: ['string', 'number', 'boolean'], - }, + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + type: { + type: 'string', + enum: ['string', 'number', 'boolean'], }, - required: ['name', 'type'], }, + required: ['name', 'type'], }, }, }, - description: { - type: 'string', - description: 'Description of what this field represents', - }, }, - required: ['name', 'type'], - additionalProperties: false, + description: { + type: 'string', + description: 'Description of what this field represents', + }, }, + required: ['name', 'type'], + additionalProperties: false, }, }, - required: ['fields'], }, + required: ['fields'], }, - temperature: { type: 'number', required: false }, - tools: { type: 'json', required: false }, }, - outputs: { - response: { - type: { - content: 'string', - model: 'string', - tokens: 'any', - toolCalls: 'any', - }, - dependsOn: { - subBlockId: 'responseFormat', - condition: { - whenEmpty: { - content: 'string', - model: 'string', - tokens: 'any', - toolCalls: 'any', - }, - whenFilled: 'json', + temperature: { type: 'number', required: false }, + tools: { type: 'json', required: false }, + }, + outputs: { + response: { + type: { + content: 'string', + model: 'string', + tokens: 'any', + toolCalls: 'any', + }, + dependsOn: { + subBlockId: 'responseFormat', + condition: { + whenEmpty: { + content: 'string', + model: 'string', + tokens: 'any', + toolCalls: 'any', }, + whenFilled: 'json', }, }, }, - subBlocks: [ - { - id: 'systemPrompt', - title: 'System Prompt', - type: 'long-input', - layout: 'full', - placeholder: 'Enter system prompt...', - }, - { - id: 'context', - title: 'User Prompt', - type: 'short-input', - layout: 'full', - placeholder: 'Enter context or user message...', - }, - { - id: 'model', - title: 'Model', - type: 'dropdown', - layout: 'half', - options: Object.keys(MODEL_TOOLS), - }, - { - id: 'temperature', - title: 'Temperature', - type: 'slider', - layout: 'half', - min: 0, - max: 2, - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your API key', - password: true, - connectionDroppable: false, - }, - { - id: 'tools', - title: 'Tools', - type: 'tool-input', - layout: 'full', - }, - { - id: 'responseFormat', - title: 'Response Format', - type: 'code', - layout: 'full', - placeholder: `{ - "fields": [ - { - "name": "sentiment", - "type": "string", - "description": "The sentiment of the text (positive, negative, neutral)" - }, - { - "name": "score", - "type": "number", - "description": "Confidence score between 0 and 1" - } - ] -}`, - }, - ], }, } diff --git a/blocks/blocks/api.ts b/blocks/blocks/api.ts index 917a39f35b..fa99674578 100644 --- a/blocks/blocks/api.ts +++ b/blocks/blocks/api.ts @@ -3,61 +3,57 @@ import { RequestResponse } from '@/tools/http/request' import { BlockConfig } from '../types' export const ApiBlock: BlockConfig = { - type: 'api', - toolbar: { - title: 'API', - description: 'Use any API', - bgColor: '#2F55FF', - icon: ApiIcon, - category: 'blocks', - }, + id: 'api', + name: 'API', + description: 'Use any API', + category: 'blocks', + bgColor: '#2F55FF', + icon: ApiIcon, + subBlocks: [ + { + id: 'url', + title: 'URL', + type: 'short-input', + layout: 'full', + placeholder: 'Enter URL', + }, + { + id: 'method', + title: 'Method', + type: 'dropdown', + layout: 'half', + options: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], + }, + { + id: 'headers', + title: 'Headers', + type: 'table', + layout: 'full', + columns: ['Key', 'Value'], + }, + { + id: 'body', + title: 'Body', + type: 'code', + layout: 'full', + }, + ], tools: { access: ['http_request'], }, - workflow: { - inputs: { - url: { type: 'string', required: true }, - method: { type: 'string', required: true }, - headers: { type: 'json', required: false }, - body: { type: 'json', required: false }, - }, - outputs: { - response: { - type: { - data: 'any', - status: 'number', - headers: 'json', - }, + inputs: { + url: { type: 'string', required: true }, + method: { type: 'string', required: true }, + headers: { type: 'json', required: false }, + body: { type: 'json', required: false }, + }, + outputs: { + response: { + type: { + data: 'any', + status: 'number', + headers: 'json', }, }, - subBlocks: [ - { - id: 'url', - title: 'URL', - type: 'short-input', - layout: 'full', - placeholder: 'Enter URL', - }, - { - id: 'method', - title: 'Method', - type: 'dropdown', - layout: 'half', - options: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], - }, - { - id: 'headers', - title: 'Headers', - type: 'table', - layout: 'full', - columns: ['Key', 'Value'], - }, - { - id: 'body', - title: 'Body', - type: 'code', - layout: 'full', - }, - ], }, } diff --git a/blocks/blocks/condition.ts b/blocks/blocks/condition.ts index e4d17f376f..b34d17e88d 100644 --- a/blocks/blocks/condition.ts +++ b/blocks/blocks/condition.ts @@ -3,36 +3,33 @@ import { CodeExecutionOutput } from '@/tools/function/execute' import { BlockConfig } from '../types' export const ConditionBlock: BlockConfig = { - type: 'condition', - toolbar: { - title: 'Condition', - description: 'Add a condition', - bgColor: '#FF972F', - icon: ConditionalIcon, - category: 'blocks', - }, + id: 'condition', + name: 'Condition', + description: 'Add a condition', + longDescription: 'Add a condition to the workflow', + bgColor: '#FF972F', + icon: ConditionalIcon, + category: 'blocks', + subBlocks: [ + { + id: 'conditions', + type: 'condition-input', + layout: 'full', + }, + ], tools: { access: ['function_execute'], }, - workflow: { - inputs: { - code: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - result: 'any', - stdout: 'string', - executionTime: 'number', - }, + inputs: { + code: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + result: 'any', + stdout: 'string', + executionTime: 'number', }, }, - subBlocks: [ - { - id: 'conditions', - type: 'condition-input', - layout: 'full', - }, - ], }, } diff --git a/blocks/blocks/crewai.ts b/blocks/blocks/crewai.ts index 74fa3f59c3..58b2cef27f 100644 --- a/blocks/blocks/crewai.ts +++ b/blocks/blocks/crewai.ts @@ -3,63 +3,59 @@ import { VisionResponse } from '@/tools/crewai/vision' import { BlockConfig } from '../types' export const CrewAIVisionBlock: BlockConfig = { - type: 'crewai_vision', - toolbar: { - title: 'CrewAI Vision Tool', - description: 'Analyze images with vision models', - bgColor: '#FF5A50', - icon: CrewAIIcon, - category: 'tools', - }, + id: 'crewai_vision', + name: 'CrewAI Vision', + description: 'Analyze images with vision models', + category: 'tools', + bgColor: '#FF5A50', + icon: CrewAIIcon, + subBlocks: [ + { + id: 'imageUrl', + title: 'Image URL', + type: 'short-input', + layout: 'full', + placeholder: 'Enter publicly accessible image URL', + }, + { + id: 'model', + title: 'Vision Model', + type: 'dropdown', + layout: 'half', + options: ['gpt-4o', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229'], + }, + { + id: 'prompt', + title: 'Prompt', + type: 'long-input', + layout: 'full', + placeholder: 'Enter prompt for image analysis (optional)', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your API key', + password: true, + }, + ], tools: { access: ['crewai_vision'], }, - workflow: { - inputs: { - apiKey: { type: 'string', required: true }, - imageUrl: { type: 'string', required: true }, - model: { type: 'string', required: false }, - prompt: { type: 'string', required: false }, - }, - outputs: { - response: { - type: { - content: 'string', - model: 'any', - tokens: 'any', - }, + inputs: { + apiKey: { type: 'string', required: true }, + imageUrl: { type: 'string', required: true }, + model: { type: 'string', required: false }, + prompt: { type: 'string', required: false }, + }, + outputs: { + response: { + type: { + content: 'string', + model: 'any', + tokens: 'any', }, }, - subBlocks: [ - { - id: 'imageUrl', - title: 'Image URL', - type: 'short-input', - layout: 'full', - placeholder: 'Enter publicly accessible image URL', - }, - { - id: 'model', - title: 'Vision Model', - type: 'dropdown', - layout: 'half', - options: ['gpt-4o', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229'], - }, - { - id: 'prompt', - title: 'Prompt', - type: 'long-input', - layout: 'full', - placeholder: 'Enter prompt for image analysis (optional)', - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your API key', - password: true, - }, - ], }, } diff --git a/blocks/blocks/evaluator.ts b/blocks/blocks/evaluator.ts index b97f7c20ca..3fa655b577 100644 --- a/blocks/blocks/evaluator.ts +++ b/blocks/blocks/evaluator.ts @@ -56,14 +56,60 @@ const generateResponseFormat = (metrics: Metric[]) => ({ }) export const EvaluatorBlock: BlockConfig = { - type: 'evaluator', - toolbar: { - title: 'Evaluator', - description: 'Evaluate content', - bgColor: '#2FA1FF', - icon: ChartBarIcon, - category: 'blocks', - }, + id: 'evaluator', + name: 'Evaluator', + description: 'Evaluate content', + category: 'blocks', + bgColor: '#2FA1FF', + icon: ChartBarIcon, + subBlocks: [ + { + id: 'metrics', + title: 'Evaluation Metrics', + type: 'eval-input', + layout: 'full', + }, + { + id: 'content', + title: 'Content', + type: 'short-input', + layout: 'full', + placeholder: 'Enter the content to evaluate', + }, + { + id: 'model', + title: 'Model', + type: 'dropdown', + layout: 'half', + options: Object.keys(MODEL_TOOLS), + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your API key', + password: true, + connectionDroppable: false, + }, + { + id: 'systemPrompt', + title: 'System Prompt', + type: 'code', + layout: 'full', + hidden: true, + value: (params: Record) => { + const metrics = params.metrics || [] + const content = params.content || '' + const responseFormat = generateResponseFormat(metrics) + + return JSON.stringify({ + systemPrompt: generateEvaluatorPrompt(metrics, content), + responseFormat, + }) + }, + }, + ], tools: { access: [ 'openai_chat', @@ -87,116 +133,66 @@ export const EvaluatorBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - metrics: { - type: 'json' as ParamType, - required: true, - description: 'Array of metrics to evaluate against', - schema: { - type: 'array', - properties: {}, - items: { - type: 'object', - properties: { - name: { - type: 'string', - description: 'Name of the metric', - }, - description: { - type: 'string', - description: 'Description of what this metric measures', - }, - range: { - type: 'object', - properties: { - min: { - type: 'number', - description: 'Minimum possible score', - }, - max: { - type: 'number', - description: 'Maximum possible score', - }, + inputs: { + metrics: { + type: 'json' as ParamType, + required: true, + description: 'Array of metrics to evaluate against', + schema: { + type: 'array', + properties: {}, + items: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Name of the metric', + }, + description: { + type: 'string', + description: 'Description of what this metric measures', + }, + range: { + type: 'object', + properties: { + min: { + type: 'number', + description: 'Minimum possible score', + }, + max: { + type: 'number', + description: 'Maximum possible score', }, - required: ['min', 'max'], }, + required: ['min', 'max'], }, - required: ['name', 'description', 'range'], - }, - }, - }, - model: { type: 'string' as ParamType, required: true }, - apiKey: { type: 'string' as ParamType, required: true }, - content: { type: 'string' as ParamType, required: true }, - }, - outputs: { - response: { - type: { - content: 'string', - model: 'string', - tokens: 'any', - }, - dependsOn: { - subBlockId: 'metrics', - condition: { - whenEmpty: { - content: 'string', - model: 'string', - tokens: 'any', - }, - whenFilled: 'json', }, + required: ['name', 'description', 'range'], }, }, }, - subBlocks: [ - { - id: 'metrics', - title: 'Evaluation Metrics', - type: 'eval-input', - layout: 'full', + model: { type: 'string' as ParamType, required: true }, + apiKey: { type: 'string' as ParamType, required: true }, + content: { type: 'string' as ParamType, required: true }, + }, + outputs: { + response: { + type: { + content: 'string', + model: 'string', + tokens: 'any', }, - { - id: 'content', - title: 'Content', - type: 'short-input', - layout: 'full', - placeholder: 'Enter the content to evaluate', - }, - { - id: 'model', - title: 'Model', - type: 'dropdown', - layout: 'half', - options: Object.keys(MODEL_TOOLS), - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your API key', - password: true, - connectionDroppable: false, - }, - { - id: 'systemPrompt', - title: 'System Prompt', - type: 'code', - layout: 'full', - hidden: true, - value: (params: Record) => { - const metrics = params.metrics || [] - const content = params.content || '' - const responseFormat = generateResponseFormat(metrics) - - return JSON.stringify({ - systemPrompt: generateEvaluatorPrompt(metrics, content), - responseFormat, - }) + dependsOn: { + subBlockId: 'metrics', + condition: { + whenEmpty: { + content: 'string', + model: 'string', + tokens: 'any', + }, + whenFilled: 'json', }, }, - ], + }, }, } diff --git a/blocks/blocks/firecrawl.ts b/blocks/blocks/firecrawl.ts index 8ae57722a0..83a5e5a705 100644 --- a/blocks/blocks/firecrawl.ts +++ b/blocks/blocks/firecrawl.ts @@ -2,55 +2,51 @@ import { FirecrawlIcon } from '@/components/icons' import { ScrapeResponse } from '@/tools/firecrawl/scrape' import { BlockConfig } from '../types' -export const FirecrawlScrapeBlock: BlockConfig = { - type: 'firecrawl_scrape', - toolbar: { - title: 'Firecrawl Scraper', - description: 'Scrape website content', - bgColor: '#181C1E', - icon: FirecrawlIcon, - category: 'tools', - }, +export const FirecrawlBlock: BlockConfig = { + id: 'firecrawl', + name: 'Firecrawl', + description: 'Scrape website content', + category: 'tools', + bgColor: '#181C1E', + icon: FirecrawlIcon, + subBlocks: [ + { + id: 'url', + title: 'Website URL', + type: 'short-input', + layout: 'full', + placeholder: 'Enter the webpage URL to scrape', + }, + { + id: 'onlyMainContent', + title: 'Only Main Content', + type: 'switch', + layout: 'half', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Firecrawl API key', + password: true, + }, + ], tools: { access: ['firecrawl_scrape'], }, - workflow: { - inputs: { - apiKey: { type: 'string', required: true }, - url: { type: 'string', required: true }, - scrapeOptions: { type: 'json', required: false }, - }, - outputs: { - response: { - type: { - markdown: 'string', - html: 'any', - metadata: 'json', - }, + inputs: { + apiKey: { type: 'string', required: true }, + url: { type: 'string', required: true }, + scrapeOptions: { type: 'json', required: false }, + }, + outputs: { + response: { + type: { + markdown: 'string', + html: 'any', + metadata: 'json', }, }, - subBlocks: [ - { - id: 'url', - title: 'Website URL', - type: 'short-input', - layout: 'full', - placeholder: 'Enter the webpage URL to scrape', - }, - { - id: 'onlyMainContent', - title: 'Only Main Content', - type: 'switch', - layout: 'half', - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Firecrawl API key', - password: true, - }, - ], }, } diff --git a/blocks/blocks/function.ts b/blocks/blocks/function.ts index 5f8bb987ae..550a686c3f 100644 --- a/blocks/blocks/function.ts +++ b/blocks/blocks/function.ts @@ -3,38 +3,34 @@ import { CodeExecutionOutput } from '@/tools/function/execute' import { BlockConfig } from '../types' export const FunctionBlock: BlockConfig = { - type: 'function', - toolbar: { - title: 'Function', - description: 'Run custom logic', - bgColor: '#FF402F', - icon: CodeIcon, - category: 'blocks', - }, + id: 'function', + name: 'Function', + description: 'Run custom logic', + category: 'blocks', + bgColor: '#FF402F', + icon: CodeIcon, + subBlocks: [ + { + id: 'code', + type: 'code', + layout: 'full', + }, + ], tools: { access: ['function_execute'], }, - workflow: { - inputs: { - code: { type: 'string', required: true }, - timeout: { type: 'number', required: false }, - memoryLimit: { type: 'number', required: false }, - }, - outputs: { - response: { - type: { - result: 'any', - stdout: 'string', - executionTime: 'number', - }, + inputs: { + code: { type: 'string', required: true }, + timeout: { type: 'number', required: false }, + memoryLimit: { type: 'number', required: false }, + }, + outputs: { + response: { + type: { + result: 'any', + stdout: 'string', + executionTime: 'number', }, }, - subBlocks: [ - { - id: 'code', - type: 'code', - layout: 'full', - }, - ], }, } diff --git a/blocks/blocks/github.ts b/blocks/blocks/github.ts index e70bb641e4..d40a6ec05c 100644 --- a/blocks/blocks/github.ts +++ b/blocks/blocks/github.ts @@ -3,57 +3,53 @@ import { GithubIcon } from '../../components/icons' import { BlockConfig } from '../types' export const GitHubBlock: BlockConfig = { - type: 'github_repo_info', - toolbar: { - title: 'GitHub Repository', - description: 'Fetch GitHub repository', - bgColor: '#181C1E', - icon: GithubIcon, - category: 'tools', - }, + id: 'github', + name: 'GitHub', + description: 'Fetch GitHub repository', + category: 'tools', + bgColor: '#181C1E', + icon: GithubIcon, + subBlocks: [ + { + id: 'owner', + title: 'Repository Owner', + type: 'short-input', + layout: 'half', + placeholder: 'e.g., microsoft', + }, + { + id: 'repo', + title: 'Repository Name', + type: 'short-input', + layout: 'half', + placeholder: 'e.g., vscode', + }, + { + id: 'apiKey', + title: 'GitHub Token', + type: 'short-input', + layout: 'full', + placeholder: 'Enter GitHub Token', + password: true, + }, + ], tools: { access: ['github_repoinfo'], }, - workflow: { - inputs: { - owner: { type: 'string', required: true }, - repo: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - name: 'string', - description: 'string', - stars: 'number', - forks: 'number', - openIssues: 'number', - language: 'string', - }, + inputs: { + owner: { type: 'string', required: true }, + repo: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + name: 'string', + description: 'string', + stars: 'number', + forks: 'number', + openIssues: 'number', + language: 'string', }, }, - subBlocks: [ - { - id: 'owner', - title: 'Repository Owner', - type: 'short-input', - layout: 'half', - placeholder: 'e.g., microsoft', - }, - { - id: 'repo', - title: 'Repository Name', - type: 'short-input', - layout: 'half', - placeholder: 'e.g., vscode', - }, - { - id: 'apiKey', - title: 'GitHub Token', - type: 'short-input', - layout: 'full', - placeholder: 'Enter GitHub Token', - password: true, - }, - ], }, } diff --git a/blocks/blocks/gmail.ts b/blocks/blocks/gmail.ts index 16970f55c5..601e437e4c 100644 --- a/blocks/blocks/gmail.ts +++ b/blocks/blocks/gmail.ts @@ -3,14 +3,86 @@ import { GmailToolResponse } from '@/tools/gmail/types' import { BlockConfig } from '../types' export const GmailBlock: BlockConfig = { - type: 'gmail_block', - toolbar: { - title: 'Gmail', - description: 'Send, read, and search Gmail messages', - bgColor: '#F14537', - icon: GmailIcon, - category: 'tools', - }, + id: 'gmail', + name: 'Gmail', + description: 'Send, read, and search Gmail messages', + category: 'tools', + bgColor: '#F14537', + icon: GmailIcon, + subBlocks: [ + // Operation selector + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Send Email', id: 'send_gmail' }, + { label: 'Read Email', id: 'read_gmail' }, + { label: 'Search Emails', id: 'search_gmail' }, + ], + }, + // OAuth Token + { + id: 'accessToken', + title: 'Access Token', + type: 'short-input', + layout: 'full', + placeholder: 'Enter Gmail OAuth token', + password: true, + }, + // Send Email Fields + { + id: 'to', + title: 'To', + type: 'short-input', + layout: 'full', + placeholder: 'Recipient email address', + condition: { field: 'operation', value: 'send_gmail' }, + }, + { + id: 'subject', + title: 'Subject', + type: 'short-input', + layout: 'full', + placeholder: 'Email subject', + condition: { field: 'operation', value: 'send_gmail' }, + }, + { + id: 'body', + title: 'Body', + type: 'long-input', + layout: 'full', + placeholder: 'Email content', + condition: { field: 'operation', value: 'send_gmail' }, + }, + // Read Email Fields + { + id: 'messageId', + title: 'Message ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter message ID to read', + condition: { field: 'operation', value: 'read_gmail' }, + }, + // Search Fields + { + id: 'query', + title: 'Search Query', + type: 'short-input', + layout: 'full', + placeholder: 'Enter search terms', + condition: { field: 'operation', value: 'search_gmail' }, + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + layout: 'full', + placeholder: 'Maximum number of results (default: 10)', + condition: { field: 'operation', value: 'search_gmail' }, + }, + ], tools: { access: ['gmail_send', 'gmail_read', 'gmail_search'], config: { @@ -28,101 +100,25 @@ export const GmailBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - operation: { type: 'string', required: true }, - accessToken: { type: 'string', required: true }, - // Send operation inputs - to: { type: 'string', required: false }, - subject: { type: 'string', required: false }, - body: { type: 'string', required: false }, - // Read operation inputs - messageId: { type: 'string', required: false }, - // Search operation inputs - query: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, - }, - outputs: { - response: { - type: { - content: 'string', - metadata: 'json', - }, + inputs: { + operation: { type: 'string', required: true }, + accessToken: { type: 'string', required: true }, + // Send operation inputs + to: { type: 'string', required: false }, + subject: { type: 'string', required: false }, + body: { type: 'string', required: false }, + // Read operation inputs + messageId: { type: 'string', required: false }, + // Search operation inputs + query: { type: 'string', required: false }, + maxResults: { type: 'number', required: false }, + }, + outputs: { + response: { + type: { + content: 'string', + metadata: 'json', }, }, - subBlocks: [ - // Operation selector - { - id: 'operation', - title: 'Operation', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Send Email', id: 'send_gmail' }, - { label: 'Read Email', id: 'read_gmail' }, - { label: 'Search Emails', id: 'search_gmail' }, - ], - }, - // OAuth Token - { - id: 'accessToken', - title: 'Access Token', - type: 'short-input', - layout: 'full', - placeholder: 'Enter Gmail OAuth token', - password: true, - }, - // Send Email Fields - { - id: 'to', - title: 'To', - type: 'short-input', - layout: 'full', - placeholder: 'Recipient email address', - condition: { field: 'operation', value: 'send_gmail' }, - }, - { - id: 'subject', - title: 'Subject', - type: 'short-input', - layout: 'full', - placeholder: 'Email subject', - condition: { field: 'operation', value: 'send_gmail' }, - }, - { - id: 'body', - title: 'Body', - type: 'long-input', - layout: 'full', - placeholder: 'Email content', - condition: { field: 'operation', value: 'send_gmail' }, - }, - // Read Email Fields - { - id: 'messageId', - title: 'Message ID', - type: 'short-input', - layout: 'full', - placeholder: 'Enter message ID to read', - condition: { field: 'operation', value: 'read_gmail' }, - }, - // Search Fields - { - id: 'query', - title: 'Search Query', - type: 'short-input', - layout: 'full', - placeholder: 'Enter search terms', - condition: { field: 'operation', value: 'search_gmail' }, - }, - { - id: 'maxResults', - title: 'Max Results', - type: 'short-input', - layout: 'full', - placeholder: 'Maximum number of results (default: 10)', - condition: { field: 'operation', value: 'search_gmail' }, - }, - ], }, } diff --git a/blocks/blocks/jina.ts b/blocks/blocks/jina.ts index 26cc0c3533..5b4ca1ccc9 100644 --- a/blocks/blocks/jina.ts +++ b/blocks/blocks/jina.ts @@ -3,59 +3,55 @@ import { ReadUrlResponse } from '@/tools/jina/reader' import { BlockConfig } from '../types' export const JinaBlock: BlockConfig = { - type: 'jina_reader', - toolbar: { - title: 'Jina Reader', - description: 'Convert website content into text', - bgColor: '#333333', - icon: JinaAIIcon, - category: 'tools', - }, + id: 'jina', + name: 'Jina', + description: 'Convert website content into text', + category: 'tools', + bgColor: '#333333', + icon: JinaAIIcon, + subBlocks: [ + { + id: 'url', + title: 'URL', + type: 'short-input', + layout: 'full', + placeholder: 'Enter URL to extract content from', + }, + { + id: 'options', + title: 'Options', + type: 'checkbox-list', + layout: 'full', + options: [ + { id: 'useReaderLMv2', label: 'Use Reader LM v2' }, + { id: 'gatherLinks', label: 'Gather Links' }, + { id: 'jsonResponse', label: 'JSON Response' }, + ], + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Jina API key', + password: true, + }, + ], tools: { access: ['jina_readurl'], }, - workflow: { - inputs: { - url: { type: 'string', required: true }, - useReaderLMv2: { type: 'boolean', required: false }, - gatherLinks: { type: 'boolean', required: false }, - jsonResponse: { type: 'boolean', required: false }, - apiKey: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - content: 'string', - }, + inputs: { + url: { type: 'string', required: true }, + useReaderLMv2: { type: 'boolean', required: false }, + gatherLinks: { type: 'boolean', required: false }, + jsonResponse: { type: 'boolean', required: false }, + apiKey: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + content: 'string', }, }, - subBlocks: [ - { - id: 'url', - title: 'URL', - type: 'short-input', - layout: 'full', - placeholder: 'Enter URL to extract content from', - }, - { - id: 'options', - title: 'Options', - type: 'checkbox-list', - layout: 'full', - options: [ - { id: 'useReaderLMv2', label: 'Use Reader LM v2' }, - { id: 'gatherLinks', label: 'Gather Links' }, - { id: 'jsonResponse', label: 'JSON Response' }, - ], - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Jina API key', - password: true, - }, - ], }, } diff --git a/blocks/blocks/notion.ts b/blocks/blocks/notion.ts index b2152a29e9..22878b4608 100644 --- a/blocks/blocks/notion.ts +++ b/blocks/blocks/notion.ts @@ -3,14 +3,47 @@ import { NotionResponse } from '@/tools/notion/read' import { BlockConfig } from '../types' export const NotionBlock: BlockConfig = { - type: 'notion_reader', - toolbar: { - title: 'Notion', - description: 'Read and write to Notion pages and databases', - bgColor: '#000000', - icon: NotionIcon, - category: 'tools', - }, + id: 'notion', + name: 'Notion', + description: 'Read and write to Notion pages and databases', + category: 'tools', + bgColor: '#000000', + icon: NotionIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Read Page', id: 'read_notion' }, + { label: 'Write Page', id: 'write_notion' }, + ], + }, + { + id: 'pageId', + title: 'Page ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter Notion page ID', + }, + { + id: 'content', + title: 'Content', + type: 'long-input', + layout: 'full', + placeholder: 'Enter content to write (for write operation)', + condition: { field: 'operation', value: 'write_notion' }, + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Notion API key', + password: true, + }, + ], tools: { access: ['notion_read', 'notion_write'], config: { @@ -19,55 +52,18 @@ export const NotionBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - pageId: { type: 'string', required: true }, - operation: { type: 'string', required: true }, - content: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - content: 'string', - metadata: 'any', - }, + inputs: { + pageId: { type: 'string', required: true }, + operation: { type: 'string', required: true }, + content: { type: 'string', required: false }, + apiKey: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + content: 'string', + metadata: 'any', }, }, - subBlocks: [ - { - id: 'operation', - title: 'Operation', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Read Page', id: 'read_notion' }, - { label: 'Write Page', id: 'write_notion' }, - ], - }, - { - id: 'pageId', - title: 'Page ID', - type: 'short-input', - layout: 'full', - placeholder: 'Enter Notion page ID', - }, - { - id: 'content', - title: 'Content', - type: 'long-input', - layout: 'full', - placeholder: 'Enter content to write (for write operation)', - condition: { field: 'operation', value: 'write_notion' }, - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Notion API key', - password: true, - }, - ], }, } diff --git a/blocks/blocks/router.ts b/blocks/blocks/router.ts index 387e96ced3..e0900b1ee8 100644 --- a/blocks/blocks/router.ts +++ b/blocks/blocks/router.ts @@ -86,14 +86,47 @@ Remember: Your response must be ONLY the block ID - no additional text, formatti } export const RouterBlock: BlockConfig = { - type: 'router', - toolbar: { - title: 'Router', - description: 'Route workflow', - bgColor: '#28C43F', - icon: ConnectIcon, - category: 'blocks', - }, + id: 'router', + name: 'Router', + description: 'Route workflow', + category: 'blocks', + bgColor: '#28C43F', + icon: ConnectIcon, + subBlocks: [ + { + id: 'prompt', + title: 'Prompt', + type: 'long-input', + layout: 'full', + placeholder: 'Route to the correct block based on the input...', + }, + { + id: 'model', + title: 'Model', + type: 'dropdown', + layout: 'half', + options: Object.keys(MODEL_TOOLS), + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your API key', + password: true, + connectionDroppable: false, + }, + { + id: 'systemPrompt', + title: 'System Prompt', + type: 'code', + layout: 'full', + hidden: true, + value: (params: Record) => { + return generateRouterPrompt(params.prompt || '') + }, + }, + ], tools: { access: [ 'openai_chat', @@ -117,56 +150,19 @@ export const RouterBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - prompt: { type: 'string', required: true }, - model: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - content: 'string', - model: 'string', - tokens: 'any', - selectedPath: 'json', - }, + inputs: { + prompt: { type: 'string', required: true }, + model: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + content: 'string', + model: 'string', + tokens: 'any', + selectedPath: 'json', }, }, - subBlocks: [ - { - id: 'prompt', - title: 'Prompt', - type: 'long-input', - layout: 'full', - placeholder: 'Route to the correct block based on the input...', - }, - { - id: 'model', - title: 'Model', - type: 'dropdown', - layout: 'half', - options: Object.keys(MODEL_TOOLS), - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your API key', - password: true, - connectionDroppable: false, - }, - { - id: 'systemPrompt', - title: 'System Prompt', - type: 'code', - layout: 'full', - hidden: true, - value: (params: Record) => { - return generateRouterPrompt(params.prompt || '') - }, - }, - ], }, } diff --git a/blocks/blocks/serper.ts b/blocks/blocks/serper.ts index 4532416030..c684bc2cc4 100644 --- a/blocks/blocks/serper.ts +++ b/blocks/blocks/serper.ts @@ -3,77 +3,73 @@ import { SearchResponse } from '@/tools/serper/search' import { BlockConfig } from '../types' export const SerperBlock: BlockConfig = { - type: 'serper_search', - toolbar: { - title: 'Serper Search', - description: 'Search the web using Serper', - bgColor: '#2B3543', - icon: SerperIcon, - category: 'tools', - }, + id: 'serper', + name: 'Serper', + description: 'Search the web using Serper', + category: 'tools', + bgColor: '#2B3543', + icon: SerperIcon, + subBlocks: [ + { + id: 'query', + title: 'Search Query', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your search query...', + }, + { + id: 'type', + title: 'Search Type', + type: 'dropdown', + layout: 'half', + options: ['search', 'news', 'places', 'images'], + }, + { + id: 'num', + title: 'Number of Results', + type: 'dropdown', + layout: 'half', + options: ['10', '20', '30', '40', '50', '100'], + }, + { + id: 'gl', + title: 'Country', + type: 'dropdown', + layout: 'half', + options: ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'ES', 'IT', 'JP', 'KR'], + }, + { + id: 'hl', + title: 'Language', + type: 'dropdown', + layout: 'half', + options: ['en', 'es', 'fr', 'de', 'it', 'pt', 'ja', 'ko', 'zh'], + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Serper API key', + password: true, + }, + ], tools: { access: ['serper_search'], }, - workflow: { - inputs: { - query: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - num: { type: 'number', required: false }, - gl: { type: 'string', required: false }, - hl: { type: 'string', required: false }, - type: { type: 'string', required: false }, - }, - outputs: { - response: { - type: { - searchResults: 'json', - }, + inputs: { + query: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + num: { type: 'number', required: false }, + gl: { type: 'string', required: false }, + hl: { type: 'string', required: false }, + type: { type: 'string', required: false }, + }, + outputs: { + response: { + type: { + searchResults: 'json', }, }, - subBlocks: [ - { - id: 'query', - title: 'Search Query', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your search query...', - }, - { - id: 'type', - title: 'Search Type', - type: 'dropdown', - layout: 'half', - options: ['search', 'news', 'places', 'images'], - }, - { - id: 'num', - title: 'Number of Results', - type: 'dropdown', - layout: 'half', - options: ['10', '20', '30', '40', '50', '100'], - }, - { - id: 'gl', - title: 'Country', - type: 'dropdown', - layout: 'half', - options: ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'ES', 'IT', 'JP', 'KR'], - }, - { - id: 'hl', - title: 'Language', - type: 'dropdown', - layout: 'half', - options: ['en', 'es', 'fr', 'de', 'it', 'pt', 'ja', 'ko', 'zh'], - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Serper API key', - password: true, - }, - ], }, } diff --git a/blocks/blocks/slack.ts b/blocks/blocks/slack.ts index 9cb7201f9a..948cc2aa86 100644 --- a/blocks/blocks/slack.ts +++ b/blocks/blocks/slack.ts @@ -2,56 +2,52 @@ import { SlackIcon } from '@/components/icons' import { SlackMessageResponse } from '@/tools/slack/message' import { BlockConfig } from '../types' -export const SlackMessageBlock: BlockConfig = { - type: 'slack_message', - toolbar: { - title: 'Slack Message', - description: 'Send a message to Slack', - bgColor: '#611f69', - icon: SlackIcon, - category: 'tools', - }, +export const SlackBlock: BlockConfig = { + id: 'slack', + name: 'Slack', + description: 'Send a message to Slack', + category: 'tools', + bgColor: '#611f69', + icon: SlackIcon, + subBlocks: [ + { + id: 'channel', + title: 'Channel', + type: 'short-input', + layout: 'full', + placeholder: 'Enter Slack channel (e.g., #general)', + }, + { + id: 'text', + title: 'Message', + type: 'long-input', + layout: 'full', + placeholder: 'Enter your alert message', + }, + { + id: 'apiKey', + title: 'OAuth Token', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Slack OAuth token', + password: true, + connectionDroppable: false, + }, + ], tools: { access: ['slack_message'], }, - workflow: { - inputs: { - apiKey: { type: 'string', required: true }, - channel: { type: 'string', required: true }, - text: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - ts: 'string', - channel: 'string', - }, + inputs: { + apiKey: { type: 'string', required: true }, + channel: { type: 'string', required: true }, + text: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + ts: 'string', + channel: 'string', }, }, - subBlocks: [ - { - id: 'channel', - title: 'Channel', - type: 'short-input', - layout: 'full', - placeholder: 'Enter Slack channel (e.g., #general)', - }, - { - id: 'text', - title: 'Message', - type: 'long-input', - layout: 'full', - placeholder: 'Enter your alert message', - }, - { - id: 'apiKey', - title: 'OAuth Token', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Slack OAuth token', - password: true, - connectionDroppable: false, - }, - ], }, } diff --git a/blocks/blocks/starter.ts b/blocks/blocks/starter.ts index 6760343c3c..4b97041a48 100644 --- a/blocks/blocks/starter.ts +++ b/blocks/blocks/starter.ts @@ -2,251 +2,247 @@ import { StartIcon } from '@/components/icons' import { BlockConfig } from '../types' export const StarterBlock: BlockConfig = { - type: 'starter', - toolbar: { - title: 'Starter', - description: 'Start workflow', - bgColor: '#2FB3FF', - icon: StartIcon, - category: 'blocks', - }, + id: 'starter', + name: 'Starter', + description: 'Start workflow', + category: 'blocks', + bgColor: '#2FB3FF', + icon: StartIcon, + subBlocks: [ + // Main trigger selector + { + id: 'startWorkflow', + title: 'Start Workflow', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Run manually', id: 'manual' }, + { label: 'On webhook call', id: 'webhook' }, + { label: 'On schedule', id: 'schedule' }, + ], + value: () => 'manual', + }, + // Webhook configuration + { + id: 'webhookPath', + title: 'Webhook Path', + type: 'short-input', + layout: 'full', + placeholder: 'Enter webhook path (e.g., /my-webhook)', + condition: { field: 'startWorkflow', value: 'webhook' }, + }, + { + id: 'webhookSecret', + title: 'Webhook Secret', + type: 'short-input', + layout: 'full', + placeholder: 'Enter a secret key for webhook security', + password: true, + condition: { field: 'startWorkflow', value: 'webhook' }, + }, + // Schedule configuration + { + id: 'scheduleType', + title: 'Frequency', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Every X Minutes', id: 'minutes' }, + { label: 'Hourly', id: 'hourly' }, + { label: 'Daily', id: 'daily' }, + { label: 'Weekly', id: 'weekly' }, + { label: 'Monthly', id: 'monthly' }, + { label: 'Custom Cron', id: 'custom' }, + ], + value: () => 'daily', + condition: { field: 'startWorkflow', value: 'schedule' }, + }, + // Minutes schedule options + { + id: 'minutesInterval', + title: 'Run Every', + type: 'short-input', + layout: 'full', + placeholder: '15 minutes', + condition: { + field: 'scheduleType', + value: 'minutes', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + { + id: 'minutesStartingAt', + title: 'Starting At', + type: 'short-input', + layout: 'full', + placeholder: '09:00', + condition: { + field: 'scheduleType', + value: 'minutes', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + // Hourly schedule options + { + id: 'hourlyMinute', + title: 'Start at Minute', + type: 'short-input', + layout: 'full', + placeholder: '00', + condition: { + field: 'scheduleType', + value: 'hourly', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + // Daily schedule options + { + id: 'dailyTime', + title: 'Time', + type: 'short-input', + layout: 'full', + placeholder: '09:00', + condition: { + field: 'scheduleType', + value: 'daily', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + // Weekly schedule options + { + id: 'weeklyDay', + title: 'Day of Week', + type: 'dropdown', + layout: 'half', + options: [ + { label: 'Monday', id: 'MON' }, + { label: 'Tuesday', id: 'TUE' }, + { label: 'Wednesday', id: 'WED' }, + { label: 'Thursday', id: 'THU' }, + { label: 'Friday', id: 'FRI' }, + { label: 'Saturday', id: 'SAT' }, + { label: 'Sunday', id: 'SUN' }, + ], + value: () => 'MON', + condition: { + field: 'scheduleType', + value: 'weekly', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + { + id: 'weeklyDayTime', + title: 'Time', + type: 'short-input', + layout: 'half', + placeholder: '09:00', + condition: { + field: 'scheduleType', + value: 'weekly', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + // Monthly schedule options + { + id: 'monthlyDay', + title: 'Day of Month', + type: 'short-input', + layout: 'half', + placeholder: '1', + condition: { + field: 'scheduleType', + value: 'monthly', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + { + id: 'monthlyTime', + title: 'Time', + type: 'short-input', + layout: 'half', + placeholder: '09:00', + condition: { + field: 'scheduleType', + value: 'monthly', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + // Custom cron options + { + id: 'cronExpression', + title: 'Cron Expression', + type: 'short-input', + layout: 'full', + placeholder: '*/15 * * * *', + condition: { + field: 'scheduleType', + value: 'custom', + and: { + field: 'startWorkflow', + value: 'schedule', + }, + }, + }, + // Timezone configuration (for all schedule types) + { + id: 'timezone', + title: 'Timezone', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'UTC', id: 'UTC' }, + { label: 'US Eastern (UTC-4)', id: 'America/New_York' }, + { label: 'US Central (UTC-5)', id: 'America/Chicago' }, + { label: 'US Mountain (UTC-6)', id: 'America/Denver' }, + { label: 'US Pacific (UTC-7)', id: 'America/Los_Angeles' }, + { label: 'London (UTC+1)', id: 'Europe/London' }, + { label: 'Paris (UTC+2)', id: 'Europe/Paris' }, + { label: 'Singapore (UTC+8)', id: 'Asia/Singapore' }, + { label: 'Tokyo (UTC+9)', id: 'Asia/Tokyo' }, + { label: 'Sydney (UTC+10)', id: 'Australia/Sydney' }, + ], + value: () => 'UTC', + condition: { field: 'startWorkflow', value: 'schedule' }, + }, + ], tools: { access: [], }, - workflow: { - inputs: { - code: { type: 'string', required: true }, - executionMode: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - result: 'any', - stdout: 'string', - executionTime: 'number', - }, + inputs: { + code: { type: 'string', required: true }, + executionMode: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + result: 'any', + stdout: 'string', + executionTime: 'number', }, }, - subBlocks: [ - // Main trigger selector - { - id: 'startWorkflow', - title: 'Start Workflow', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Run manually', id: 'manual' }, - { label: 'On webhook call', id: 'webhook' }, - { label: 'On schedule', id: 'schedule' }, - ], - value: () => 'manual', - }, - // Webhook configuration - { - id: 'webhookPath', - title: 'Webhook Path', - type: 'short-input', - layout: 'full', - placeholder: 'Enter webhook path (e.g., /my-webhook)', - condition: { field: 'startWorkflow', value: 'webhook' }, - }, - { - id: 'webhookSecret', - title: 'Webhook Secret', - type: 'short-input', - layout: 'full', - placeholder: 'Enter a secret key for webhook security', - password: true, - condition: { field: 'startWorkflow', value: 'webhook' }, - }, - // Schedule configuration - { - id: 'scheduleType', - title: 'Frequency', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Every X Minutes', id: 'minutes' }, - { label: 'Hourly', id: 'hourly' }, - { label: 'Daily', id: 'daily' }, - { label: 'Weekly', id: 'weekly' }, - { label: 'Monthly', id: 'monthly' }, - { label: 'Custom Cron', id: 'custom' }, - ], - value: () => 'daily', - condition: { field: 'startWorkflow', value: 'schedule' }, - }, - // Minutes schedule options - { - id: 'minutesInterval', - title: 'Run Every', - type: 'short-input', - layout: 'full', - placeholder: '15 minutes', - condition: { - field: 'scheduleType', - value: 'minutes', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - { - id: 'minutesStartingAt', - title: 'Starting At', - type: 'short-input', - layout: 'full', - placeholder: '09:00', - condition: { - field: 'scheduleType', - value: 'minutes', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - // Hourly schedule options - { - id: 'hourlyMinute', - title: 'Start at Minute', - type: 'short-input', - layout: 'full', - placeholder: '00', - condition: { - field: 'scheduleType', - value: 'hourly', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - // Daily schedule options - { - id: 'dailyTime', - title: 'Time', - type: 'short-input', - layout: 'full', - placeholder: '09:00', - condition: { - field: 'scheduleType', - value: 'daily', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - // Weekly schedule options - { - id: 'weeklyDay', - title: 'Day of Week', - type: 'dropdown', - layout: 'half', - options: [ - { label: 'Monday', id: 'MON' }, - { label: 'Tuesday', id: 'TUE' }, - { label: 'Wednesday', id: 'WED' }, - { label: 'Thursday', id: 'THU' }, - { label: 'Friday', id: 'FRI' }, - { label: 'Saturday', id: 'SAT' }, - { label: 'Sunday', id: 'SUN' }, - ], - value: () => 'MON', - condition: { - field: 'scheduleType', - value: 'weekly', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - { - id: 'weeklyDayTime', - title: 'Time', - type: 'short-input', - layout: 'half', - placeholder: '09:00', - condition: { - field: 'scheduleType', - value: 'weekly', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - // Monthly schedule options - { - id: 'monthlyDay', - title: 'Day of Month', - type: 'short-input', - layout: 'half', - placeholder: '1', - condition: { - field: 'scheduleType', - value: 'monthly', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - { - id: 'monthlyTime', - title: 'Time', - type: 'short-input', - layout: 'half', - placeholder: '09:00', - condition: { - field: 'scheduleType', - value: 'monthly', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - // Custom cron options - { - id: 'cronExpression', - title: 'Cron Expression', - type: 'short-input', - layout: 'full', - placeholder: '*/15 * * * *', - condition: { - field: 'scheduleType', - value: 'custom', - and: { - field: 'startWorkflow', - value: 'schedule', - }, - }, - }, - // Timezone configuration (for all schedule types) - { - id: 'timezone', - title: 'Timezone', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'UTC', id: 'UTC' }, - { label: 'US Eastern (UTC-4)', id: 'America/New_York' }, - { label: 'US Central (UTC-5)', id: 'America/Chicago' }, - { label: 'US Mountain (UTC-6)', id: 'America/Denver' }, - { label: 'US Pacific (UTC-7)', id: 'America/Los_Angeles' }, - { label: 'London (UTC+1)', id: 'Europe/London' }, - { label: 'Paris (UTC+2)', id: 'Europe/Paris' }, - { label: 'Singapore (UTC+8)', id: 'Asia/Singapore' }, - { label: 'Tokyo (UTC+9)', id: 'Asia/Tokyo' }, - { label: 'Sydney (UTC+10)', id: 'Australia/Sydney' }, - ], - value: () => 'UTC', - condition: { field: 'startWorkflow', value: 'schedule' }, - }, - ], }, } diff --git a/blocks/blocks/tavily.ts b/blocks/blocks/tavily.ts index 05aa289ba9..25544e5727 100644 --- a/blocks/blocks/tavily.ts +++ b/blocks/blocks/tavily.ts @@ -5,14 +5,70 @@ import { BlockConfig } from '../types' type TavilyResponse = TavilySearchResponse | TavilyExtractResponse export const TavilyBlock: BlockConfig = { - type: 'tavily_block', - toolbar: { - title: 'Tavily', - description: 'Search and extract information using Tavily AI', - bgColor: '#0066FF', - icon: TavilyIcon, - category: 'tools', - }, + id: 'tavily', + name: 'Tavily', + description: 'Search and extract information using Tavily AI', + category: 'tools', + bgColor: '#0066FF', + icon: TavilyIcon, + subBlocks: [ + // Operation selector + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Search', id: 'tavily_search' }, + { label: 'Extract Content', id: 'tavily_extract' }, + ], + value: () => 'tavily_search', + }, + // API Key (common) + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Tavily API key', + password: true, + }, + // Search operation inputs + { + id: 'query', + title: 'Search Query', + type: 'long-input', + layout: 'full', + placeholder: 'Enter your search query...', + condition: { field: 'operation', value: 'tavily_search' }, + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + layout: 'full', + placeholder: '5', + condition: { field: 'operation', value: 'tavily_search' }, + }, + // Extract operation inputs + { + id: 'urls', + title: 'URL', + type: 'long-input', + layout: 'full', + placeholder: 'Enter URL to extract content from...', + condition: { field: 'operation', value: 'tavily_extract' }, + }, + { + id: 'extract_depth', + title: 'Extract Depth', + type: 'dropdown', + layout: 'full', + options: ['basic', 'advanced'], + value: () => 'basic', + condition: { field: 'operation', value: 'tavily_extract' }, + }, + ], tools: { access: ['tavily_search', 'tavily_extract'], config: { @@ -28,86 +84,26 @@ export const TavilyBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - operation: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - // Search operation - query: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, - // Extract operation - urls: { type: 'string', required: false }, - extract_depth: { type: 'string', required: false }, - }, - outputs: { - response: { - type: { - results: 'json', - answer: 'any', - query: 'string', - content: 'string', - title: 'string', - url: 'string', - }, + inputs: { + operation: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + // Search operation + query: { type: 'string', required: false }, + maxResults: { type: 'number', required: false }, + // Extract operation + urls: { type: 'string', required: false }, + extract_depth: { type: 'string', required: false }, + }, + outputs: { + response: { + type: { + results: 'json', + answer: 'any', + query: 'string', + content: 'string', + title: 'string', + url: 'string', }, }, - subBlocks: [ - // Operation selector - { - id: 'operation', - title: 'Operation', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Search', id: 'tavily_search' }, - { label: 'Extract Content', id: 'tavily_extract' }, - ], - value: () => 'tavily_search', - }, - // API Key (common) - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Tavily API key', - password: true, - }, - // Search operation inputs - { - id: 'query', - title: 'Search Query', - type: 'long-input', - layout: 'full', - placeholder: 'Enter your search query...', - condition: { field: 'operation', value: 'tavily_search' }, - }, - { - id: 'maxResults', - title: 'Max Results', - type: 'short-input', - layout: 'full', - placeholder: '5', - condition: { field: 'operation', value: 'tavily_search' }, - }, - // Extract operation inputs - { - id: 'urls', - title: 'URL', - type: 'long-input', - layout: 'full', - placeholder: 'Enter URL to extract content from...', - condition: { field: 'operation', value: 'tavily_extract' }, - }, - { - id: 'extract_depth', - title: 'Extract Depth', - type: 'dropdown', - layout: 'full', - options: ['basic', 'advanced'], - value: () => 'basic', - condition: { field: 'operation', value: 'tavily_extract' }, - }, - ], }, } diff --git a/blocks/blocks/translate.ts b/blocks/blocks/translate.ts index f1eb3f62f8..f848020994 100644 --- a/blocks/blocks/translate.ts +++ b/blocks/blocks/translate.ts @@ -15,14 +15,54 @@ const getTranslationPrompt = ( Only return the translated text without any explanations or notes. The translation should be natural and fluent in ${targetLanguage || 'English'}.` export const TranslateBlock: BlockConfig = { - type: 'translate', - toolbar: { - title: 'Translate', - description: 'Translate text to any language', - bgColor: '#FF4B4B', - icon: TranslateIcon, - category: 'tools', - }, + id: 'translate', + name: 'Translate', + description: 'Translate text to any language', + category: 'tools', + bgColor: '#FF4B4B', + icon: TranslateIcon, + subBlocks: [ + { + id: 'context', + title: 'Text to Translate', + type: 'long-input', + layout: 'full', + placeholder: 'Enter the text you want to translate', + }, + { + id: 'targetLanguage', + title: 'Translate To', + type: 'short-input', + layout: 'full', + placeholder: 'Enter language (e.g. Spanish, French, etc.)', + }, + { + id: 'model', + title: 'Model', + type: 'dropdown', + layout: 'half', + options: Object.keys(MODEL_TOOLS), + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your API key', + password: true, + connectionDroppable: false, + }, + { + id: 'systemPrompt', + title: 'System Prompt', + type: 'code', + layout: 'full', + hidden: true, + value: (params: Record) => { + return getTranslationPrompt(params.targetLanguage || 'English') + }, + }, + ], tools: { access: ['openai_chat', 'anthropic_chat', 'google_chat'], config: { @@ -43,63 +83,19 @@ export const TranslateBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - context: { type: 'string', required: true }, - targetLanguage: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - systemPrompt: { type: 'string', required: true }, - }, - outputs: { - response: { - type: { - content: 'string', - model: 'string', - tokens: 'any', - }, + inputs: { + context: { type: 'string', required: true }, + targetLanguage: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + systemPrompt: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + content: 'string', + model: 'string', + tokens: 'any', }, }, - subBlocks: [ - { - id: 'context', - title: 'Text to Translate', - type: 'long-input', - layout: 'full', - placeholder: 'Enter the text you want to translate', - }, - { - id: 'targetLanguage', - title: 'Translate To', - type: 'short-input', - layout: 'full', - placeholder: 'Enter language (e.g. Spanish, French, etc.)', - }, - { - id: 'model', - title: 'Model', - type: 'dropdown', - layout: 'half', - options: Object.keys(MODEL_TOOLS), - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your API key', - password: true, - connectionDroppable: false, - }, - { - id: 'systemPrompt', - title: 'System Prompt', - type: 'code', - layout: 'full', - hidden: true, - value: (params: Record) => { - return getTranslationPrompt(params.targetLanguage || 'English') - }, - }, - ], }, } diff --git a/blocks/blocks/x.ts b/blocks/blocks/x.ts index 6c4dd1244e..a4ab0fce22 100644 --- a/blocks/blocks/x.ts +++ b/blocks/blocks/x.ts @@ -5,14 +5,140 @@ import { BlockConfig } from '../types' type XResponse = XWriteResponse | XReadResponse | XSearchResponse | XUserResponse export const XBlock: BlockConfig = { - type: 'x_block', - toolbar: { - title: 'X', - description: 'Interact with X', - bgColor: '#000000', // X's black color - icon: xIcon, - category: 'tools', - }, + id: 'x', + name: 'X', + description: 'Interact with X', + category: 'tools', + bgColor: '#000000', // X's black color + icon: xIcon, + subBlocks: [ + // Operation selector + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Post a New Tweet', id: 'x_write' }, + { label: 'Get Tweet Details', id: 'x_read' }, + { label: 'Search Tweets', id: 'x_search' }, + { label: 'Get User Profile', id: 'x_user' }, + ], + value: () => 'x_write', + }, + // API Key (common) + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your X Bearer token', + password: true, + }, + // Write operation inputs + { + id: 'text', + title: 'Tweet Text', + type: 'long-input', + layout: 'full', + placeholder: "What's happening?", + condition: { field: 'operation', value: 'x_write' }, + }, + { + id: 'replyTo', + title: 'Reply To (Tweet ID)', + type: 'short-input', + layout: 'full', + placeholder: 'Enter tweet ID to reply to', + condition: { field: 'operation', value: 'x_write' }, + }, + { + id: 'mediaIds', + title: 'Media IDs', + type: 'short-input', + layout: 'full', + placeholder: 'Enter comma-separated media IDs', + condition: { field: 'operation', value: 'x_write' }, + }, + // Read operation inputs + { + id: 'tweetId', + title: 'Tweet ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter tweet ID to read', + condition: { field: 'operation', value: 'x_read' }, + }, + { + id: 'includeReplies', + title: 'Include Replies', + type: 'dropdown', + layout: 'full', + options: ['true', 'false'], + value: () => 'false', + condition: { field: 'operation', value: 'x_read' }, + }, + // Search operation inputs + { + id: 'query', + title: 'Search Query', + type: 'long-input', + layout: 'full', + placeholder: 'Enter search terms (supports X search operators)', + condition: { field: 'operation', value: 'x_search' }, + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + layout: 'full', + placeholder: '10', + condition: { field: 'operation', value: 'x_search' }, + }, + { + id: 'sortOrder', + title: 'Sort Order', + type: 'dropdown', + layout: 'full', + options: ['recency', 'relevancy'], + value: () => 'recency', + condition: { field: 'operation', value: 'x_search' }, + }, + { + id: 'startTime', + title: 'Start Time', + type: 'short-input', + layout: 'full', + placeholder: 'YYYY-MM-DDTHH:mm:ssZ', + condition: { field: 'operation', value: 'x_search' }, + }, + { + id: 'endTime', + title: 'End Time', + type: 'short-input', + layout: 'full', + placeholder: 'YYYY-MM-DDTHH:mm:ssZ', + condition: { field: 'operation', value: 'x_search' }, + }, + // User operation inputs + { + id: 'username', + title: 'Username', + type: 'short-input', + layout: 'full', + placeholder: 'Enter username (without @)', + condition: { field: 'operation', value: 'x_user' }, + }, + { + id: 'includeRecentTweets', + title: 'Include Recent Tweets', + type: 'dropdown', + layout: 'full', + options: ['true', 'false'], + value: () => 'false', + condition: { field: 'operation', value: 'x_user' }, + }, + ], tools: { access: ['x_write', 'x_read', 'x_search', 'x_user'], config: { @@ -32,169 +158,39 @@ export const XBlock: BlockConfig = { }, }, }, - workflow: { - inputs: { - operation: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - // Write operation - text: { type: 'string', required: false }, - replyTo: { type: 'string', required: false }, - mediaIds: { type: 'string', required: false }, - poll: { type: 'json', required: false }, - // Read operation - tweetId: { type: 'string', required: false }, - includeReplies: { type: 'boolean', required: false }, - // Search operation - query: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, - startTime: { type: 'string', required: false }, - endTime: { type: 'string', required: false }, - sortOrder: { type: 'string', required: false }, - // User operation - username: { type: 'string', required: false }, - includeRecentTweets: { type: 'boolean', required: false }, - }, - outputs: { - response: { - type: { - tweet: 'json', - replies: 'any', - context: 'any', - tweets: 'json', - includes: 'any', - meta: 'json', - user: 'json', - recentTweets: 'any', - }, + inputs: { + operation: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + // Write operation + text: { type: 'string', required: false }, + replyTo: { type: 'string', required: false }, + mediaIds: { type: 'string', required: false }, + poll: { type: 'json', required: false }, + // Read operation + tweetId: { type: 'string', required: false }, + includeReplies: { type: 'boolean', required: false }, + // Search operation + query: { type: 'string', required: false }, + maxResults: { type: 'number', required: false }, + startTime: { type: 'string', required: false }, + endTime: { type: 'string', required: false }, + sortOrder: { type: 'string', required: false }, + // User operation + username: { type: 'string', required: false }, + includeRecentTweets: { type: 'boolean', required: false }, + }, + outputs: { + response: { + type: { + tweet: 'json', + replies: 'any', + context: 'any', + tweets: 'json', + includes: 'any', + meta: 'json', + user: 'json', + recentTweets: 'any', }, }, - subBlocks: [ - // Operation selector - { - id: 'operation', - title: 'Operation', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Post a New Tweet', id: 'x_write' }, - { label: 'Get Tweet Details', id: 'x_read' }, - { label: 'Search Tweets', id: 'x_search' }, - { label: 'Get User Profile', id: 'x_user' }, - ], - value: () => 'x_write', - }, - // API Key (common) - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your X Bearer token', - password: true, - }, - // Write operation inputs - { - id: 'text', - title: 'Tweet Text', - type: 'long-input', - layout: 'full', - placeholder: "What's happening?", - condition: { field: 'operation', value: 'x_write' }, - }, - { - id: 'replyTo', - title: 'Reply To (Tweet ID)', - type: 'short-input', - layout: 'full', - placeholder: 'Enter tweet ID to reply to', - condition: { field: 'operation', value: 'x_write' }, - }, - { - id: 'mediaIds', - title: 'Media IDs', - type: 'short-input', - layout: 'full', - placeholder: 'Enter comma-separated media IDs', - condition: { field: 'operation', value: 'x_write' }, - }, - // Read operation inputs - { - id: 'tweetId', - title: 'Tweet ID', - type: 'short-input', - layout: 'full', - placeholder: 'Enter tweet ID to read', - condition: { field: 'operation', value: 'x_read' }, - }, - { - id: 'includeReplies', - title: 'Include Replies', - type: 'dropdown', - layout: 'full', - options: ['true', 'false'], - value: () => 'false', - condition: { field: 'operation', value: 'x_read' }, - }, - // Search operation inputs - { - id: 'query', - title: 'Search Query', - type: 'long-input', - layout: 'full', - placeholder: 'Enter search terms (supports X search operators)', - condition: { field: 'operation', value: 'x_search' }, - }, - { - id: 'maxResults', - title: 'Max Results', - type: 'short-input', - layout: 'full', - placeholder: '10', - condition: { field: 'operation', value: 'x_search' }, - }, - { - id: 'sortOrder', - title: 'Sort Order', - type: 'dropdown', - layout: 'full', - options: ['recency', 'relevancy'], - value: () => 'recency', - condition: { field: 'operation', value: 'x_search' }, - }, - { - id: 'startTime', - title: 'Start Time', - type: 'short-input', - layout: 'full', - placeholder: 'YYYY-MM-DDTHH:mm:ssZ', - condition: { field: 'operation', value: 'x_search' }, - }, - { - id: 'endTime', - title: 'End Time', - type: 'short-input', - layout: 'full', - placeholder: 'YYYY-MM-DDTHH:mm:ssZ', - condition: { field: 'operation', value: 'x_search' }, - }, - // User operation inputs - { - id: 'username', - title: 'Username', - type: 'short-input', - layout: 'full', - placeholder: 'Enter username (without @)', - condition: { field: 'operation', value: 'x_user' }, - }, - { - id: 'includeRecentTweets', - title: 'Include Recent Tweets', - type: 'dropdown', - layout: 'full', - options: ['true', 'false'], - value: () => 'false', - condition: { field: 'operation', value: 'x_user' }, - }, - ], }, } diff --git a/blocks/blocks/youtube.ts b/blocks/blocks/youtube.ts index 461b2e05d0..6a36667bc9 100644 --- a/blocks/blocks/youtube.ts +++ b/blocks/blocks/youtube.ts @@ -2,56 +2,52 @@ import { YouTubeIcon } from '@/components/icons' import { YouTubeSearchResponse } from '@/tools/youtube/search' import { BlockConfig } from '../types' -export const YouTubeSearchBlock: BlockConfig = { - type: 'youtube_search', - toolbar: { - title: 'YouTube Search', - description: 'Search for videos on YouTube', - bgColor: '#FF0000', - icon: YouTubeIcon, - category: 'tools', - }, +export const YouTubeBlock: BlockConfig = { + id: 'youtube', + name: 'YouTube', + description: 'Search for videos on YouTube', + category: 'tools', + bgColor: '#FF0000', + icon: YouTubeIcon, + subBlocks: [ + { + id: 'query', + title: 'Search Query', + type: 'short-input', + layout: 'full', + placeholder: 'Enter search query', + }, + { + id: 'apiKey', + title: 'YouTube API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter YouTube API Key', + password: true, + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'slider', + layout: 'half', + min: 0, + max: 20, + }, + ], tools: { access: ['youtube_search'], }, - workflow: { - inputs: { - apiKey: { type: 'string', required: true }, - query: { type: 'string', required: true }, - maxResults: { type: 'number', required: false }, - }, - outputs: { - response: { - type: { - items: 'json', - totalResults: 'number', - }, + inputs: { + apiKey: { type: 'string', required: true }, + query: { type: 'string', required: true }, + maxResults: { type: 'number', required: false }, + }, + outputs: { + response: { + type: { + items: 'json', + totalResults: 'number', }, }, - subBlocks: [ - { - id: 'query', - title: 'Search Query', - type: 'short-input', - layout: 'full', - placeholder: 'Enter search query', - }, - { - id: 'apiKey', - title: 'YouTube API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter YouTube API Key', - password: true, - }, - { - id: 'maxResults', - title: 'Max Results', - type: 'slider', - layout: 'half', - min: 0, - max: 20, - }, - ], }, } diff --git a/blocks/index.ts b/blocks/index.ts index fa965bb243..f79f31b889 100644 --- a/blocks/index.ts +++ b/blocks/index.ts @@ -4,7 +4,7 @@ import { ApiBlock } from './blocks/api' import { ConditionBlock } from './blocks/condition' import { CrewAIVisionBlock } from './blocks/crewai' import { EvaluatorBlock } from './blocks/evaluator' -import { FirecrawlScrapeBlock } from './blocks/firecrawl' +import { FirecrawlBlock } from './blocks/firecrawl' import { FunctionBlock } from './blocks/function' import { GitHubBlock } from './blocks/github' import { GmailBlock } from './blocks/gmail' @@ -12,12 +12,12 @@ import { JinaBlock } from './blocks/jina' import { NotionBlock } from './blocks/notion' import { RouterBlock } from './blocks/router' import { SerperBlock } from './blocks/serper' -import { SlackMessageBlock } from './blocks/slack' +import { SlackBlock } from './blocks/slack' import { StarterBlock } from './blocks/starter' import { TavilyBlock } from './blocks/tavily' import { TranslateBlock } from './blocks/translate' import { XBlock } from './blocks/x' -import { YouTubeSearchBlock } from './blocks/youtube' +import { YouTubeBlock } from './blocks/youtube' import { BlockConfig } from './types' // Export blocks for ease of use @@ -26,17 +26,17 @@ export { ApiBlock, FunctionBlock, CrewAIVisionBlock, - FirecrawlScrapeBlock, + FirecrawlBlock, JinaBlock, TranslateBlock, - SlackMessageBlock, + SlackBlock, GitHubBlock, ConditionBlock, SerperBlock, TavilyBlock, RouterBlock, EvaluatorBlock, - YouTubeSearchBlock, + YouTubeBlock, NotionBlock, GmailBlock, XBlock, @@ -53,27 +53,27 @@ const blocks: Record = { router: RouterBlock, evaluator: EvaluatorBlock, crewai_vision: CrewAIVisionBlock, - firecrawl_scrape: FirecrawlScrapeBlock, - jina_reader: JinaBlock, + firecrawl: FirecrawlBlock, + jina: JinaBlock, translate: TranslateBlock, - slack_message: SlackMessageBlock, - github_repo_info: GitHubBlock, - serper_search: SerperBlock, - tavily_block: TavilyBlock, - youtube_search: YouTubeSearchBlock, - notion_reader: NotionBlock, - gmail_block: GmailBlock, - x_block: XBlock, + slack: SlackBlock, + github: GitHubBlock, + serper: SerperBlock, + tavily: TavilyBlock, + youtube: YouTubeBlock, + notion: NotionBlock, + gmail: GmailBlock, + x: XBlock, } // Helper functions -export const getBlock = (type: string): BlockConfig | undefined => blocks[type] +export const getBlock = (id: string): BlockConfig | undefined => blocks[id] export const getBlocksByCategory = (category: 'blocks' | 'tools'): BlockConfig[] => - Object.values(blocks).filter((block) => block.toolbar.category === category) + Object.values(blocks).filter((block) => block.category === category) -export const getAllBlockTypes = (): string[] => Object.keys(blocks) +export const getAllBlockIds = (): string[] => Object.keys(blocks) -export const isValidBlockType = (type: string): type is string => type in blocks +export const isValidBlockId = (id: string): id is string => id in blocks export const getAllBlocks = (): BlockConfig[] => Object.values(blocks) diff --git a/blocks/types.ts b/blocks/types.ts index 9efe6407a9..93eca3edcd 100644 --- a/blocks/types.ts +++ b/blocks/types.ts @@ -2,30 +2,35 @@ import type { SVGProps } from 'react' import type { JSX } from 'react' import { ToolResponse } from '@/tools/types' -// Basic type definitions for block components +// Basic types export type BlockIcon = (props: SVGProps) => JSX.Element -export type BlockCategory = 'blocks' | 'tools' export type ParamType = 'string' | 'number' | 'boolean' | 'json' export type PrimitiveValueType = 'string' | 'number' | 'boolean' | 'json' | 'any' -// Sub-block configuration types +// Block classification +export type BlockCategory = 'blocks' | 'tools' + +// SubBlock types export type SubBlockType = - | 'short-input' - | 'long-input' - | 'dropdown' - | 'slider' - | 'table' - | 'code' - | 'switch' - | 'tool-input' - | 'checkbox-list' - | 'condition-input' - | 'eval-input' + | 'short-input' // Single line input + | 'long-input' // Multi-line input + | 'dropdown' // Select menu + | 'slider' // Range input + | 'table' // Grid layout + | 'code' // Code editor + | 'switch' // Toggle button + | 'tool-input' // Tool configuration + | 'checkbox-list' // Multiple selection + | 'condition-input' // Conditional logic + | 'eval-input' // Evaluation input + +// Component width setting export type SubBlockLayout = 'full' | 'half' -// Tool output type utilities +// Tool result extraction export type ExtractToolOutput = T extends ToolResponse ? T['output'] : never +// Convert tool output to types export type ToolOutputToValueType = T extends Record ? { @@ -41,11 +46,12 @@ export type ToolOutputToValueType = } : never -// Block configuration interfaces and types +// Block output definition export type BlockOutput = | PrimitiveValueType | { [key: string]: PrimitiveValueType | Record } +// Parameter validation rules export interface ParamConfig { type: ParamType required: boolean @@ -64,6 +70,7 @@ export interface ParamConfig { } } +// SubBlock configuration export interface SubBlockConfig { id: string title?: string @@ -88,39 +95,38 @@ export interface SubBlockConfig { } } +// Main block definition export interface BlockConfig { - type: string - toolbar: { - title: string - description: string - bgColor: string - icon: BlockIcon - category: BlockCategory - } + id: string + name: string + description: string + category: BlockCategory + longDescription?: string + bgColor: string + icon: BlockIcon + subBlocks: SubBlockConfig[] tools: { access: string[] config?: { tool: (params: Record) => string } } - workflow: { - subBlocks: SubBlockConfig[] - inputs: Record - outputs: { - response: { - type: ToolOutputToValueType> - dependsOn?: { - subBlockId: string - condition: { - whenEmpty: ToolOutputToValueType> - whenFilled: 'json' - } + inputs: Record + outputs: { + response: { + type: ToolOutputToValueType> + dependsOn?: { + subBlockId: string + condition: { + whenEmpty: ToolOutputToValueType> + whenFilled: 'json' } } } } } +// Output configuration rules export interface OutputConfig { type: BlockOutput dependsOn?: { diff --git a/executor/index.ts b/executor/index.ts index 3414481840..5140bffbcf 100644 --- a/executor/index.ts +++ b/executor/index.ts @@ -270,7 +270,7 @@ export class Executor { context.blockStates.set(blockId, result) lastOutput = result - if (block.metadata?.type === 'router') { + if (block.metadata?.id === 'router') { const routerResult = result as { response: { content: string @@ -280,7 +280,7 @@ export class Executor { } } routerDecisions.set(block.id, routerResult.response.selectedPath.blockId) - } else if (block.metadata?.type === 'condition') { + } else if (block.metadata?.id === 'condition') { const conditionResult = await this.executeConditionalBlock(block, context) activeConditionalPaths.set(block.id, conditionResult.selectedConditionId) } @@ -324,7 +324,7 @@ export class Executor { // Check if this was the last block in the loop (e.g., a condition block) const isLoopComplete = executedLoopBlocks.some((blockId) => { const block = blocks.find((b) => b.id === blockId) - return block?.metadata?.type === 'condition' + return block?.metadata?.id === 'condition' }) if (hasLoopConnection) { @@ -374,7 +374,7 @@ export class Executor { context: ExecutionContext ): Promise { if (block.enabled === false) { - throw new Error(`Cannot execute disabled block: ${block.metadata?.title || block.id}`) + throw new Error(`Cannot execute disabled block: ${block.metadata?.name || block.id}`) } const blockLog = this.startBlockLog(block) @@ -383,7 +383,7 @@ export class Executor { let output: BlockOutput // Execute block based on its type. - if (block.metadata?.type === 'router') { + if (block.metadata?.id === 'router') { const routerOutput = await this.executeRouterBlock(block, context) output = { response: { @@ -393,10 +393,10 @@ export class Executor { selectedPath: routerOutput.selectedPath, }, } - } else if (block.metadata?.type === 'evaluator') { + } else if (block.metadata?.id === 'evaluator') { const evaluatorOutput = await this.executeEvaluatorBlock(block, context) output = evaluatorOutput - } else if (block.metadata?.type === 'condition') { + } else if (block.metadata?.id === 'condition') { const conditionResult = await this.executeConditionalBlock(block, context) output = { response: { @@ -409,7 +409,7 @@ export class Executor { }, }, } - } else if (block.metadata?.type === 'agent') { + } else if (block.metadata?.id === 'agent') { // Agent block: use a provider request. let responseFormat: any = undefined if (inputs.responseFormat) { @@ -431,7 +431,7 @@ export class Executor { const formattedTools = Array.isArray(inputs.tools) ? inputs.tools .map((tool: any) => { - const blockFound = getAllBlocks().find((b: BlockConfig) => b.type === tool.type) + const blockFound = getAllBlocks().find((b: BlockConfig) => b.id === tool.type) const toolId = blockFound?.tools.access[0] if (!toolId) return null @@ -580,8 +580,8 @@ export class Executor { } return { id: targetBlock.id, - type: targetBlock.metadata?.type, - title: targetBlock.metadata?.title, + type: targetBlock.metadata?.id, + title: targetBlock.metadata?.name, description: targetBlock.metadata?.description, subBlocks: targetBlock.config.params, currentState: context.blockStates.get(targetBlock.id), @@ -729,7 +729,7 @@ export class Executor { for (const conn of connections) { // Don't follow connections from other routers const sourceBlock = this.workflow.blocks.find((b) => b.id === conn.source) - if (sourceBlock?.metadata?.type !== 'router') { + if (sourceBlock?.metadata?.id !== 'router') { queue.push(conn.target) } } @@ -784,8 +784,8 @@ export class Executor { if (!sourceBlock) { throw new Error(`Source block ${sourceBlockId} not found`) } - const sourceKey = sourceBlock.metadata?.title - ? sourceBlock.metadata.title.toLowerCase().replace(/\s+/g, '') + const sourceKey = sourceBlock.metadata?.name + ? sourceBlock.metadata.name.toLowerCase().replace(/\s+/g, '') : 'source' const outgoingConnections = this.workflow.connections.filter((conn) => conn.source === block.id) @@ -874,8 +874,8 @@ export class Executor { result: conditionMet, selectedPath: { blockId: targetBlock.id, - blockType: targetBlock.metadata?.type || '', - blockTitle: targetBlock.metadata?.title || '', + blockType: targetBlock.metadata?.id || '', + blockTitle: targetBlock.metadata?.name || '', }, selectedConditionId: selectedCondition.id, }, @@ -892,8 +892,8 @@ export class Executor { sourceOutput: sourceBlockState, selectedPath: { blockId: targetBlock.id, - blockType: targetBlock.metadata?.type || '', - blockTitle: targetBlock.metadata?.title || '', + blockType: targetBlock.metadata?.id || '', + blockTitle: targetBlock.metadata?.name || '', }, } } @@ -918,7 +918,7 @@ export class Executor { const blockById = new Map(this.workflow.blocks.map((b) => [b.id, b])) const blockByName = new Map( this.workflow.blocks.map((b) => [ - b.metadata?.title?.toLowerCase().replace(/\s+/g, '') || '', + b.metadata?.name?.toLowerCase().replace(/\s+/g, '') || '', b, ]) ) @@ -932,8 +932,8 @@ export class Executor { blockById, blockByName, context.blockStates, - block.metadata?.title || '', - block.metadata?.type || '' + block.metadata?.name || '', + block.metadata?.id || '' ) // Resolve environment variables @@ -961,8 +961,8 @@ export class Executor { private startBlockLog(block: SerializedBlock): BlockLog { return { blockId: block.id, - blockTitle: block.metadata?.title || '', - blockType: block.metadata?.type || '', + blockName: block.metadata?.name || '', + blockType: block.metadata?.id || '', startedAt: new Date().toISOString(), endedAt: '', durationMs: 0, @@ -986,7 +986,7 @@ export class Executor { ) { const sourceBlock = blocks.find((b) => b.id === conn.source) - if (sourceBlock?.metadata?.type === 'router') { + if (sourceBlock?.metadata?.id === 'router') { const chosenPath = routerDecisions?.get(sourceBlock.id) if (conn.target === chosenPath) { diff --git a/executor/types.ts b/executor/types.ts index c0103d00ff..93bea0504a 100644 --- a/executor/types.ts +++ b/executor/types.ts @@ -5,7 +5,7 @@ import { BlockOutput } from '@/blocks/types' */ export interface BlockLog { blockId: string - blockTitle?: string + blockName?: string blockType?: string startedAt: string endedAt: string diff --git a/serializer/index.ts b/serializer/index.ts index a788c283b9..bd64aa7d04 100644 --- a/serializer/index.ts +++ b/serializer/index.ts @@ -38,8 +38,8 @@ export class Serializer { // Get inputs from block config const inputs: Record = {} - if (blockConfig.workflow.inputs) { - Object.entries(blockConfig.workflow.inputs).forEach(([key, config]) => { + if (blockConfig.inputs) { + Object.entries(blockConfig.inputs).forEach(([key, config]) => { inputs[key] = config.type }) } @@ -62,11 +62,11 @@ export class Serializer { : {}), }, metadata: { - title: block.name, - description: blockConfig.toolbar.description, - category: blockConfig.toolbar.category, - color: blockConfig.toolbar.bgColor, - type: block.type, + id: block.type, + name: block.name, + description: blockConfig.description, + category: blockConfig.category, + color: blockConfig.bgColor, }, enabled: block.enabled, } @@ -86,7 +86,7 @@ export class Serializer { }) // Then check for any subBlocks with default values - blockConfig.workflow.subBlocks.forEach((subBlockConfig) => { + blockConfig.subBlocks.forEach((subBlockConfig) => { const id = subBlockConfig.id if (params[id] === null && subBlockConfig.value) { // If the value is null and there's a default value function, use it @@ -125,9 +125,9 @@ export class Serializer { } private deserializeBlock(serializedBlock: SerializedBlock): BlockState { - const blockType = serializedBlock.metadata?.type + const blockType = serializedBlock.metadata?.id if (!blockType) { - throw new Error(`Invalid block type: ${serializedBlock.metadata?.type}`) + throw new Error(`Invalid block type: ${serializedBlock.metadata?.id}`) } const blockConfig = getBlock(blockType) @@ -136,7 +136,7 @@ export class Serializer { } const subBlocks: Record = {} - blockConfig.workflow.subBlocks.forEach((subBlock) => { + blockConfig.subBlocks.forEach((subBlock) => { subBlocks[subBlock.id] = { id: subBlock.id, type: subBlock.type, @@ -147,7 +147,7 @@ export class Serializer { return { id: serializedBlock.id, type: blockType, - name: serializedBlock.metadata?.title || blockConfig.toolbar.title, + name: serializedBlock.metadata?.name || blockConfig.name, position: serializedBlock.position, subBlocks, outputs: serializedBlock.outputs, diff --git a/serializer/types.ts b/serializer/types.ts index a33efb60df..ea52cce5af 100644 --- a/serializer/types.ts +++ b/serializer/types.ts @@ -29,12 +29,12 @@ export interface SerializedBlock { inputs: Record outputs: Record metadata?: { - title?: string + id: string + name?: string description?: string category?: string icon?: string color?: string - type: string } enabled: boolean } diff --git a/stores/workflow/store.ts b/stores/workflow/store.ts index d39efc30dc..52b4d701d6 100644 --- a/stores/workflow/store.ts +++ b/stores/workflow/store.ts @@ -104,7 +104,7 @@ export const useWorkflowStore = create()( if (!blockConfig) return const subBlocks: Record = {} - blockConfig.workflow.subBlocks.forEach((subBlock) => { + blockConfig.subBlocks.forEach((subBlock) => { const subBlockId = subBlock.id subBlocks[subBlockId] = { id: subBlockId, @@ -113,7 +113,7 @@ export const useWorkflowStore = create()( } }) - const outputs = resolveOutputType(blockConfig.workflow.outputs, subBlocks) + const outputs = resolveOutputType(blockConfig.outputs, subBlocks) const newState = { blocks: {