mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
Changed block types config for simplicity
This commit is contained in:
+17
-26
@@ -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 <Icon className={className} />
|
||||
}
|
||||
|
||||
// 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 (
|
||||
<div className="w-full">
|
||||
{selectedTools.length === 0 ? (
|
||||
@@ -161,17 +155,17 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<CommandGroup>
|
||||
{toolBlocks.map((block) => (
|
||||
<CommandItem
|
||||
key={block.type}
|
||||
key={block.id}
|
||||
onSelect={() => handleSelectTool(block)}
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-center w-6 h-6 rounded"
|
||||
style={{ backgroundColor: block.toolbar.bgColor }}
|
||||
style={{ backgroundColor: block.bgColor }}
|
||||
>
|
||||
<IconComponent icon={block.toolbar.icon} className="w-4 h-4 text-white" />
|
||||
<IconComponent icon={block.icon} className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span>{block.toolbar.title}</span>
|
||||
<span>{block.name}</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -182,7 +176,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
) : (
|
||||
<div className="flex flex-wrap gap-2 min-h-[2.5rem] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background">
|
||||
{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) {
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="flex items-center justify-center w-5 h-5 rounded"
|
||||
style={{ backgroundColor: toolBlock?.toolbar.bgColor }}
|
||||
style={{ backgroundColor: toolBlock?.bgColor }}
|
||||
>
|
||||
<IconComponent
|
||||
icon={toolBlock?.toolbar.icon}
|
||||
className="w-3 h-3 text-white"
|
||||
/>
|
||||
<IconComponent icon={toolBlock?.icon} className="w-3 h-3 text-white" />
|
||||
</div>
|
||||
<span className="text-sm font-medium">{tool.title}</span>
|
||||
</div>
|
||||
@@ -278,17 +269,17 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<CommandGroup>
|
||||
{toolBlocks.map((block) => (
|
||||
<CommandItem
|
||||
key={block.type}
|
||||
key={block.id}
|
||||
onSelect={() => handleSelectTool(block)}
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-center w-6 h-6 rounded"
|
||||
style={{ backgroundColor: block.toolbar.bgColor }}
|
||||
style={{ backgroundColor: block.bgColor }}
|
||||
>
|
||||
<IconComponent icon={block.toolbar.icon} className="w-4 h-4 text-white" />
|
||||
<IconComponent icon={block.icon} className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span>{block.toolbar.title}</span>
|
||||
<span>{block.name}</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
@@ -21,7 +21,6 @@ interface WorkflowBlockProps {
|
||||
// Combine both interfaces into a single component
|
||||
export function WorkflowBlock({ id, data, selected }: NodeProps<WorkflowBlockProps>) {
|
||||
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<WorkflowBlockPro
|
||||
return rows
|
||||
}
|
||||
|
||||
const subBlockRows = groupSubBlocks(workflow.subBlocks, id)
|
||||
const subBlockRows = groupSubBlocks(config.subBlocks, id)
|
||||
|
||||
// Name editing handlers
|
||||
const handleNameClick = () => {
|
||||
@@ -209,9 +208,9 @@ export function WorkflowBlock({ id, data, selected }: NodeProps<WorkflowBlockPro
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="flex items-center justify-center w-7 h-7 rounded"
|
||||
style={{ backgroundColor: isEnabled ? toolbar.bgColor : 'gray' }}
|
||||
style={{ backgroundColor: isEnabled ? config.bgColor : 'gray' }}
|
||||
>
|
||||
<toolbar.icon className="w-5 h-5 text-white" />
|
||||
<config.icon className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<input
|
||||
|
||||
@@ -227,8 +227,8 @@ function WorkflowContent() {
|
||||
}
|
||||
|
||||
const id = crypto.randomUUID()
|
||||
const name = `${blockConfig.toolbar.title} ${
|
||||
Object.values(blocks).filter((b) => 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)
|
||||
|
||||
@@ -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 ? (
|
||||
<AlertCircle className="h-4 w-4 text-destructive" />
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ChevronDown, ChevronRight } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
interface JSONViewProps {
|
||||
|
||||
@@ -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) {
|
||||
>
|
||||
<div
|
||||
className="relative flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg"
|
||||
style={{ backgroundColor: config.toolbar.bgColor }}
|
||||
style={{ backgroundColor: config.bgColor }}
|
||||
>
|
||||
<config.toolbar.icon
|
||||
<config.icon
|
||||
className={`text-white transition-transform duration-200 group-hover:scale-110 ${
|
||||
config.type === 'agent' ? 'w-[24px] h-[24px]' : 'w-[22px] h-[22px]'
|
||||
config.id === 'agent' ? 'w-[24px] h-[24px]' : 'w-[22px] h-[22px]'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="font-medium leading-none">{config.toolbar.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{config.toolbar.description}</p>
|
||||
<h3 className="font-medium leading-none">{config.name}</h3>
|
||||
<p className="text-sm text-muted-foreground">{config.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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() {
|
||||
<div className="p-4 pb-20">
|
||||
<div className="flex flex-col gap-3">
|
||||
{blocks.map((block) => (
|
||||
<ToolbarBlock key={block.type} config={block} />
|
||||
<ToolbarBlock key={block.id} config={block} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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,
|
||||
|
||||
+133
-151
@@ -23,14 +23,64 @@ interface AgentResponse extends ToolResponse {
|
||||
}
|
||||
|
||||
export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
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<AgentResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+47
-51
@@ -3,61 +3,57 @@ import { RequestResponse } from '@/tools/http/request'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const ApiBlock: BlockConfig<RequestResponse> = {
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+23
-26
@@ -3,36 +3,33 @@ import { CodeExecutionOutput } from '@/tools/function/execute'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const ConditionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+49
-53
@@ -3,63 +3,59 @@ import { VisionResponse } from '@/tools/crewai/vision'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const CrewAIVisionBlock: BlockConfig<VisionResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+105
-109
@@ -56,14 +56,60 @@ const generateResponseFormat = (metrics: Metric[]) => ({
|
||||
})
|
||||
|
||||
export const EvaluatorBlock: BlockConfig<EvaluatorResponse> = {
|
||||
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<string, any>) => {
|
||||
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<EvaluatorResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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<string, any>) => {
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+41
-45
@@ -2,55 +2,51 @@ import { FirecrawlIcon } from '@/components/icons'
|
||||
import { ScrapeResponse } from '@/tools/firecrawl/scrape'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const FirecrawlScrapeBlock: BlockConfig<ScrapeResponse> = {
|
||||
type: 'firecrawl_scrape',
|
||||
toolbar: {
|
||||
title: 'Firecrawl Scraper',
|
||||
description: 'Scrape website content',
|
||||
bgColor: '#181C1E',
|
||||
icon: FirecrawlIcon,
|
||||
category: 'tools',
|
||||
},
|
||||
export const FirecrawlBlock: BlockConfig<ScrapeResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+24
-28
@@ -3,38 +3,34 @@ import { CodeExecutionOutput } from '@/tools/function/execute'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const FunctionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+43
-47
@@ -3,57 +3,53 @@ import { GithubIcon } from '../../components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+98
-102
@@ -3,14 +3,86 @@ import { GmailToolResponse } from '@/tools/gmail/types'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
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<GmailToolResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+45
-49
@@ -3,59 +3,55 @@ import { ReadUrlResponse } from '@/tools/jina/reader'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+52
-56
@@ -3,14 +3,47 @@ import { NotionResponse } from '@/tools/notion/read'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const NotionBlock: BlockConfig<NotionResponse> = {
|
||||
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<NotionResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+53
-57
@@ -86,14 +86,47 @@ Remember: Your response must be ONLY the block ID - no additional text, formatti
|
||||
}
|
||||
|
||||
export const RouterBlock: BlockConfig<RouterResponse> = {
|
||||
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<string, any>) => {
|
||||
return generateRouterPrompt(params.prompt || '')
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'openai_chat',
|
||||
@@ -117,56 +150,19 @@ export const RouterBlock: BlockConfig<RouterResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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<string, any>) => {
|
||||
return generateRouterPrompt(params.prompt || '')
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+63
-67
@@ -3,77 +3,73 @@ import { SearchResponse } from '@/tools/serper/search'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+42
-46
@@ -2,56 +2,52 @@ import { SlackIcon } from '@/components/icons'
|
||||
import { SlackMessageResponse } from '@/tools/slack/message'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const SlackMessageBlock: BlockConfig<SlackMessageResponse> = {
|
||||
type: 'slack_message',
|
||||
toolbar: {
|
||||
title: 'Slack Message',
|
||||
description: 'Send a message to Slack',
|
||||
bgColor: '#611f69',
|
||||
icon: SlackIcon,
|
||||
category: 'tools',
|
||||
},
|
||||
export const SlackBlock: BlockConfig<SlackMessageResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+237
-241
@@ -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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+83
-87
@@ -5,14 +5,70 @@ import { BlockConfig } from '../types'
|
||||
type TavilyResponse = TavilySearchResponse | TavilyExtractResponse
|
||||
|
||||
export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
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<TavilyResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+60
-64
@@ -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<ChatResponse> = {
|
||||
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<string, any>) => {
|
||||
return getTranslationPrompt(params.targetLanguage || 'English')
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['openai_chat', 'anthropic_chat', 'google_chat'],
|
||||
config: {
|
||||
@@ -43,63 +83,19 @@ export const TranslateBlock: BlockConfig<ChatResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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<string, any>) => {
|
||||
return getTranslationPrompt(params.targetLanguage || 'English')
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+166
-170
@@ -5,14 +5,140 @@ import { BlockConfig } from '../types'
|
||||
type XResponse = XWriteResponse | XReadResponse | XSearchResponse | XUserResponse
|
||||
|
||||
export const XBlock: BlockConfig<XResponse> = {
|
||||
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<XResponse> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+42
-46
@@ -2,56 +2,52 @@ import { YouTubeIcon } from '@/components/icons'
|
||||
import { YouTubeSearchResponse } from '@/tools/youtube/search'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const YouTubeSearchBlock: BlockConfig<YouTubeSearchResponse> = {
|
||||
type: 'youtube_search',
|
||||
toolbar: {
|
||||
title: 'YouTube Search',
|
||||
description: 'Search for videos on YouTube',
|
||||
bgColor: '#FF0000',
|
||||
icon: YouTubeIcon,
|
||||
category: 'tools',
|
||||
},
|
||||
export const YouTubeBlock: BlockConfig<YouTubeSearchResponse> = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+20
-20
@@ -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<string, BlockConfig> = {
|
||||
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)
|
||||
|
||||
+42
-36
@@ -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<SVGSVGElement>) => 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> = T extends ToolResponse ? T['output'] : never
|
||||
|
||||
// Convert tool output to types
|
||||
export type ToolOutputToValueType<T> =
|
||||
T extends Record<string, any>
|
||||
? {
|
||||
@@ -41,11 +46,12 @@ export type ToolOutputToValueType<T> =
|
||||
}
|
||||
: never
|
||||
|
||||
// Block configuration interfaces and types
|
||||
// Block output definition
|
||||
export type BlockOutput =
|
||||
| PrimitiveValueType
|
||||
| { [key: string]: PrimitiveValueType | Record<string, any> }
|
||||
|
||||
// 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<T extends ToolResponse = ToolResponse> {
|
||||
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, any>) => string
|
||||
}
|
||||
}
|
||||
workflow: {
|
||||
subBlocks: SubBlockConfig[]
|
||||
inputs: Record<string, ParamConfig>
|
||||
outputs: {
|
||||
response: {
|
||||
type: ToolOutputToValueType<ExtractToolOutput<T>>
|
||||
dependsOn?: {
|
||||
subBlockId: string
|
||||
condition: {
|
||||
whenEmpty: ToolOutputToValueType<ExtractToolOutput<T>>
|
||||
whenFilled: 'json'
|
||||
}
|
||||
inputs: Record<string, ParamConfig>
|
||||
outputs: {
|
||||
response: {
|
||||
type: ToolOutputToValueType<ExtractToolOutput<T>>
|
||||
dependsOn?: {
|
||||
subBlockId: string
|
||||
condition: {
|
||||
whenEmpty: ToolOutputToValueType<ExtractToolOutput<T>>
|
||||
whenFilled: 'json'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output configuration rules
|
||||
export interface OutputConfig {
|
||||
type: BlockOutput
|
||||
dependsOn?: {
|
||||
|
||||
+24
-24
@@ -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<BlockOutput> {
|
||||
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) {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { BlockOutput } from '@/blocks/types'
|
||||
*/
|
||||
export interface BlockLog {
|
||||
blockId: string
|
||||
blockTitle?: string
|
||||
blockName?: string
|
||||
blockType?: string
|
||||
startedAt: string
|
||||
endedAt: string
|
||||
|
||||
+12
-12
@@ -38,8 +38,8 @@ export class Serializer {
|
||||
|
||||
// Get inputs from block config
|
||||
const inputs: Record<string, any> = {}
|
||||
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<string, any> = {}
|
||||
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,
|
||||
|
||||
+2
-2
@@ -29,12 +29,12 @@ export interface SerializedBlock {
|
||||
inputs: Record<string, ParamType>
|
||||
outputs: Record<string, BlockOutput>
|
||||
metadata?: {
|
||||
title?: string
|
||||
id: string
|
||||
name?: string
|
||||
description?: string
|
||||
category?: string
|
||||
icon?: string
|
||||
color?: string
|
||||
type: string
|
||||
}
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
|
||||
if (!blockConfig) return
|
||||
|
||||
const subBlocks: Record<string, SubBlockState> = {}
|
||||
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<WorkflowStoreWithHistory>()(
|
||||
}
|
||||
})
|
||||
|
||||
const outputs = resolveOutputType(blockConfig.workflow.outputs, subBlocks)
|
||||
const outputs = resolveOutputType(blockConfig.outputs, subBlocks)
|
||||
|
||||
const newState = {
|
||||
blocks: {
|
||||
|
||||
Reference in New Issue
Block a user